利用底层内存池的C++自定义分配器

0xb*_*00d 7 c++ memory-management memory-pool

我正在使用一个内存池类,它重用已分配的内存地址和一个包装该类的自定义分配器.以下代码段为您提供了界面的基本概念.

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.
*/}
Run Code Online (Sandbox Code Playgroud)

当然,对此类需求的限制是有限的.但是,它在你需要的情况下非常有用: - 为一个以非常天真的方式使用该分配器的类指定一个分配器类型(例如,即使它是可取的,也避免分配较大的分块). - 重复分配和释放相同大小的内存. - 您希望使用分配器的类型是非常小的(例如内置类型,如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;
Run Code Online (Sandbox Code Playgroud)

std::listallocactor::allocate(1, 0)每次push_back打电话都叫.unique()确保触摸每个元素并与下一个元素进行比较.然而,结果令人失望.管理块状分配内存池所需的最小开销大于系统获得的任何可能的优势.

你能想到一个会提高性能的场景吗?

编辑: 当然,它比它快得多std::allocator.

spr*_*aff 1

C++0x 对内存池等作用域分配器有更好的支持。

分析您的代码,除非您的算法执行非常规则的分配/释放模式(例如 LIFO),否则很难预测这将带来什么优势

当所有分配的对象大小相同时,编写一个非常快的分配器非常容易。有一次我写了一些类似的东西

template <size_t ObjectSize> class allocator {
    // ...
};

template <typename T> class allocator : public allocator <sizeof (T)> {
    // ...
};
Run Code Online (Sandbox Code Playgroud)

在设计分配器之前,您需要确定将分配什么以及如何分配。答案operator new是“任何事情”和“无论如何”,这就是为什么它有时不是最佳的。如果您不能正确回答这些问题,您的分配器可能不会有很大的改进。