Запрошен опыт Boost Pool. Полезен ли он в качестве распределителя с предварительным выделением?

Недавно я искал механизм пула/распределителя. Boost Pool, похоже, предлагает решение, но есть еще вещи, которые он не смог вывести из документации.

Что нужно выделить

  1. Несколько небольших классов (~30 символов)
  2. std::map (я хочу убедиться, что он сам по себе не выполняет динамический распределитель)
  3. выделение внутри pugi::xml
  4. станд::строки

Как контролировать адресное пространство для выделения (или просто количество)

Кажется, что object_pool обеспечивает хороший способ распределения потребности 1) Однако хотелось бы установить фиксированный размер для использования распределителя. По умолчанию он захватывает память сам. Если возможно, я хотел бы дать ему адресное пространство, в котором он может играть.

char * mem_for_class[1024*1024];
boost::object_pool<my_class,mem_for_class> q;

or:

const int max_no_objs=1024;
boost::object_pool<my_class,max_no_objs> q;

Хотя UserAllocator доступен в Boost::Pool; это кажется поражение точки. Я боюсь, что необходимый контроль сделает его слишком неэффективным... и было бы лучше начать с нуля.

Можно ли установить фиксированную область для pool_allocator?

Вопрос немного похож на первый. Предоставляет ли пул boost какой-либо способ ограничения объема/где выделенной памяти при предоставлении boost::pool_allocator классу стандартного типа (например, карте)

Мой сценарий

Программирование встроенного Linux. Система должна продолжать работать вечно. Таким образом, мы не можем рисковать какой-либо сегментацией памяти. В настоящее время я в основном использую статическое распределение (стек), но также несколько необработанных новостей. Я хотел бы схему распределения, которая гарантирует, что я использую одну и ту же область памяти каждый раз, когда программа зацикливается. Скорость/пространство важны, но безопасность по-прежнему остается главным приоритетом.

Я надеюсь, что StackOverflow — это место, где можно спросить. Я безуспешно пытался связаться с автором Boost::Pool Stephen. Я не нашел ни одного форума, посвященного Boost.


person Steffen Villadsen    schedule 16.02.2014    source источник


Ответы (1)


Вы всегда можете создать распределитель, который работает с STL. Если он работает с STL, он должен работать с повышением, поскольку вы можете передавать распределители повышения в контейнеры STL.

Учитывая вышеизложенное, распределитель, который может выделять по указанному адресу памяти И имеет указанное вами ограничение по размеру, можно записать следующим образом:

#include <iostream>
#include <vector>

template<typename T>
class CAllocator
{
    private:
        std::size_t size;
        T* data = nullptr;

    public:
        typedef T* pointer;
        typedef const T* const_pointer;

        typedef T& reference;
        typedef const T& const_reference;

        typedef std::size_t size_type;
        typedef std::ptrdiff_t difference_type;

        typedef T value_type;


        CAllocator() {}
        CAllocator(pointer data_ptr, size_type max_size) noexcept : size(max_size), data(data_ptr) {};

        template<typename U>
        CAllocator(const CAllocator<U>& other) noexcept {};

        CAllocator(const CAllocator &other) : size(other.size), data(other.data) {}

        template<typename U>
        struct rebind {typedef CAllocator<U> other;};

        pointer allocate(size_type n, const void* hint = 0) {return &data[0];}
        void deallocate(void* ptr, size_type n) {}
        size_type max_size() const {return size;}
};

template <typename T, typename U>
inline bool operator == (const CAllocator<T>&, const CAllocator<U>&) {return true;}

template <typename T, typename U>
inline bool operator != (const CAllocator<T>& a, const CAllocator<U>& b) {return !(a == b);}





int main()
{
    const int size = 1024 / 4;
    int ptr[size];
    std::vector<int, CAllocator<int>> vec(CAllocator<int>(&ptr[0], size));

    int ptr2[size];
    std::vector<int, CAllocator<int>> vec2(CAllocator<int>(&ptr2[0], size));

    vec.push_back(10);
    vec.push_back(20);
    vec2.push_back(30);
    vec2.push_back(40);


    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }

    std::cout<<"\n\n";

    vec2 = vec;

    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }

    std::cout<<"\n\n";
    vec2.clear();

    vec2.push_back(100);
    vec2.push_back(200);

    for (std::size_t i = 0; i < vec2.size(); ++i)
    {
        int* val = &ptr2[i];
        std::cout<<*val<<"\n";
    }
}

Этот распределитель гарантирует, что вся память будет выделена по указанному адресу. Не более чем указанная вами сумма может быть выделена со свободой выделения, если вы хотите, находится ли она в стеке или в куче.

Вы можете создать свой собственный пул или использовать std::unique_ptr в качестве пула для одного контейнера.

EDIT: для строк вам нужно смещение sizeof(_Rep_base). См.: Почему std::string выделяется дважды?

и http://ideone.com/QWtxWg.

Он определяется как:

struct _Rep_base
{
    std::size_t     _M_length;
    std::size_t     _M_capacity;
    _Atomic_word        _M_refcount;
};

Таким образом, пример становится:

struct Repbase
{
    std::size_t     length;
    std::size_t     capacity;
    std::int16_t    refcount;
};

int main()
{
    typedef std::basic_string<char, std::char_traits<char>, CAllocator<char>> CAString;

    const int size = 1024;
    char ptr[size] = {0};

    CAString str(CAllocator<char>(&ptr[0], size));
    str = "Hello";

    std::cout<<&ptr[sizeof(Repbase)];
}
person Brandon    schedule 04.04.2014
comment
Странно, allocate ... {return &data[0];} — разве это не всегда возвращает один и тот же адрес? - person ArtemGr; 05.04.2014
comment
да. Он всегда будет возвращать один и тот же адрес. Однако, насколько я могу судить, все контейнеры STL отслеживают свою собственную память и вызывают выделение только тогда, когда им нужно больше памяти. В противном случае они вызывают конструкцию для создания элементов. Попробуйте и посмотрите. - person Brandon; 05.04.2014
comment
Хотя это и не связано с boost::pool, СПАСИБО! Прекрасный пример std::vector. Я пробовал только кратко, но я вижу, как содержимое памяти меняется с помощью отладчика. Кстати: извините за поздний ответ, ответ попал в начало вакатоина. Однако мне все еще нужна небольшая помощь (или самостоятельное изучение), чтобы понять, как использовать std::string (или, скорее, настроить basic_string для использования этого распределителя). - person Steffen Villadsen; 18.04.2014
comment
ideone.com/QWtxWg См. здесь, почему вам нужно это смещение для строк: stackoverflow.com/questions/21979330/ - person Brandon; 18.04.2014
comment
Еще раз спасибо за быструю настройку! После вашего первого сообщения я попробовал что-то подобное, но у меня возникли проблемы с синтаксисом/параметрами шаблона. Использование смещения кажется достаточно ясным на основе объявленной структуры RepBase. Строка по-прежнему может выводиться с использованием самого класса str std::cout<<str; Есть ли особая причина, по которой вы индексируете память для выделения? CAString str(CAllocator<char>(ptr, size)); // seem to work for me Классу строк (или его распределителю) не удается обнаружить нехватку места, что приводит к ошибке сегментации. Я думаю, это, однако, не хуже, чем старые cstrings. - person Steffen Villadsen; 19.04.2014
comment
Еще одна вещь меня удивляет. Ваш распределитель, похоже, не наследует/не реализует и интерфейс распределителя. Хотя у него есть методы, он не объявлен... или я что-то пропустил? - person Steffen Villadsen; 19.04.2014
comment
Я опробую еще несколько std::classes как map в ближайшие дни. - person Steffen Villadsen; 19.04.2014
comment
Вам не нужен весь интерфейс распределителя, потому что allocator_traits автоматически добавляет то, чего не хватает. - person Brandon; 19.04.2014