Распределитель, который управляет одним блоком памяти

Из-за системных ограничений предположим, что я могу выделить память из кучи только один раз (например, с помощью std::allocator или другого более общего распределителя, совместимого с C++11).

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

Мне удалось написать очень простой аллокатор, который пошагово «отдает» памяти смещением указателя. В этом аллокаторе deallocate нет операций, и память из блока не возвращается в блок. Очевидно, что можно сделать лучше, чем это. Другими словами, мне нужна управляемая куча.

Повторное использование этой блочной памяти в последовательности является сложной проблемой, поскольку необходимо управлять прерывистыми свободными сегментами, дефрагментацией, (необязательно) потокобезопасностью и т. д.

Как называется этот шаблон? Какое-то время я думал, что это был распределитель пула, но мне кажется, что это относится к чему-то другому (повторному использованию небольших объектов).

Какие функции или стандартные библиотеки C++ я могу использовать для реализации и администрирования такого распределения или, по крайней мере, создать свои собственные без особых усилий?

Я ожидал найти что-то в Boost. Но Boost.Pool — это что-то другое, и похоже, что-то вроде этого реализовано для определенной цели в Boost.Interprocess, но это не кажется простым в использовании, и мне трудно понять это вне их прототипного использования (например, межпроцессная разделяемая память.)

В противном случае самое близкое, что я нашел, это https://www.boost.org/doc/libs/1_41_0/libs/pool/doc/interfaces/pool_alloc.html , но кажется, что ::new можно вызывать несколько раз.

Пример кода:

int main(){
    UserBlockAllocator<double> a(new double[1000], 1000); 
    {
        std::vector<double, UserBlockAllocator<double>> v0(600, a);
    } // v0 returns memory to block managed by a
    std::vector<double, UserBlockAllocator<double>> v1(600, a);
    std::vector<double, UserBlockAllocator<double>> v2(600, a); //out of memory
}

person alfC    schedule 11.08.2018    source источник


Ответы (1)


Этот шаблон называется распределителем арены или распределителем стека. Если я правильно понимаю материал std::pmr, с этим связан std::pmr::monotonic_buffer_resource, но Я никогда этого не пробовал.

С этими ключевыми словами вы что-то найдете, но у меня нет опыта работы с инструментами.

Обратите внимание, что легко удалить последнее выделение.

Мощным паттерном является композиция распределителей, описанная в развлекательном докладе Андрея Александреску на CppCon 2015. Если вы хотите создать собственный инструмент, вы можете рассмотреть комбинацию FreeListAllocator (43:18) поверх вашего StackAllocator (35:42). Таким образом, вы можете решить проблему управления прерывистыми бесплатными сегментами (как вы ее описываете).

person Julius    schedule 11.08.2018
comment
Я предполагаю, что это называется распределителем стека, потому что распределитель (а не memroy) может находиться в стеке. Кроме того, monotonic_buffer_resource работает только с std::allocator. - person alfC; 13.08.2018
comment
@alfC: я не уверен, откуда взялся распределитель стека имен. Вы заметили, что распределитель стека может освобождать только самое последнее выделение? Возможно, это поведение является тем, на что ссылается стек. - person Julius; 13.08.2018
comment
@alfC: он называется распределителем стека, потому что его поведение очень похоже на стек: выделение - это операция push. Просто нет эквивалента поп-музыке. - person Nicol Bolas; 06.04.2020
comment
@NicolBolas, да, теперь я это понимаю. В то время я искал что-то, что реализовало бы stack_allocator (или что-то более продвинутое) поверх любого существующего распределителя и указателя (не только std::allocator и T*). Я не мог найти его, поэтому я реализовал его сам, насколько мог. - person alfC; 06.04.2020
comment
@alfC: Но monotonic_buffer_resource - это распределитель стека. Зачем реализовывать самому? - person Nicol Bolas; 06.04.2020
comment
@NicolBolas, потому что распределитель, который я пытаюсь использовать за monotonic_buffer_resource (или распределитель стека), не возвращает T* (на allocate). Возможно, я пытаюсь построить распределитель стека на неправильном уровне абстракции. - person alfC; 06.04.2020
comment
@alfC: monotonic_buffer_resource не занимается T*s; он имеет дело с void*, как и его родительский класс memory_resource. polymorphic_allocator<T> имеет дело с T*s, но это то, что вы используете с конструкцией, поддерживающей Allocator, и она обертывает интерфейс memory_resource способом, поддерживающим Allocator. - person Nicol Bolas; 06.04.2020
comment
@NicolBolas, да, void*, что даже ниже уровня абстракции. Я должен был сказать, потому что ресурс, который я пытаюсь использовать за monotonic_buffer_resource (или распределителем стека), не возвращает void* (а fancy_ptr‹void›). - person alfC; 06.04.2020