Пользовательский распределитель C ++, который использует базовый пул памяти

Я использую класс пула памяти, который повторно использует выделенные адреса памяти и настраиваемый распределитель, который обертывает этот класс. Следующий фрагмент кода дает вам общее представление об интерфейсе.

template<class alloc>
class memory_pool
    : boost::noncopyable,
      public allocator_traits<void>
{
public:
    memory_pool(typename alloc::size_type alloc_size);
    memory_pool(typename alloc::size_type alloc_size, alloc const&);
    template<typename U> memory_pool(typename alloc::size_type alloc_size,
        typename alloc::rebind<U>::other const&);
    virtual ~memory_pool();

    pointer allocate  (); /*throw(std::bad_alloc)*/
    void    collect   ();
    void    deallocate(pointer) throw(); /*noexcept*/
};

pointer allocate()
{/*
    Checks if a suitable chunk of memory is available in a internal linked list.
    If true, then the chunk is returned and the next chunk moves up.
    Otherwise, new memory is allocated by the underlying allocator.
*/}

void deallocate(pointer)
{/*
    Interprets the passed pointer as a chunk of memory and stores it in a linked list.
    Please note that memory isn't actually deallocated.
*/}

void collect()
{/*
    Effectively deallocates the cunks in the linked list.
    This will be called at least once during destruction.
*/}

Конечно, потребность в чем-то подобном ограничена. Однако это очень полезно в ситуациях, когда вам нужно кому: - Укажите тип распределителя для класса, который использует этот распределитель очень наивным образом (например, избегает размещение более крупных частей, даже если это было бы целесообразно). - Повторно выделять и освобождать один и тот же объем памяти. - Тип, для которого вы хотите использовать распределитель, имеет очень маленький размер (например, встроенные типы, такие как char, short, int и т. Д.).

Теоретически, реализация может использовать memory_pool, который выделяет кратное фактический размер выделения, каждый раз, когда это необходимо (из основного диспетчера памяти). Объекты, которые расположены близко друг к другу, больше подходят для любого алгоритма кеширования и / или предварительной выборки. Я реализовал такой пул памяти с некоторыми накладными расходами для обработки правильного выделения, разделения и освобождения (мы не можем освободить каждый адрес, который пользователь передаст для освобождения. Нам нужно освободить только те адреса, которые являются началом каждого блока памяти, который у нас есть были выделены ранее).

Я проверил оба случая с помощью следующего действительно простого кода:

std::list<int, allocator<int>> list;

std::clock_t t = std::clock();
for (int i = 0; i < 1 << 16; ++i)
{
    for (int j = 0; j < 1 << 16; ++j)
        list.push_back(j);
    list.unique();
    for (int j = 0; j < 1 << 16; ++j)
        list.pop_back();
}
std::cout << (std::clock() - t) / CLOCKS_PER_SEC << std::endl;

std :: list вызывает allocactor :: allocate (1, 0) каждый раз push_back называется. unique () гарантирует, что каждый элемент будет затронут и сравнится со следующим элементом. Однако результат разочаровал. Минимальные накладные расходы, необходимые для управления пулом памяти с поблочным распределением, больше, чем любое возможное преимущество, которое получает система.

Можете ли вы представить сценарий, в котором это улучшит производительность?

EDIT: Конечно, это намного быстрее, чем std :: allocator .

7
задан 0xbadf00d 6 July 2011 в 12:54
поделиться