I use a memory pool class that reuses allocated memory addresses and a custom allocator that wraps this class. The following code snippet gives you the basic idea of โโan interface.
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 (); void collect (); void deallocate(pointer) throw(); }; pointer allocate() {} void deallocate(pointer) {} void collect() {}
Of course, the need for something like this is limited. However, this is very useful in situations where you need to: - Determine the type of allocator for a class that uses this allocator is very naive (for example, Avoids highlighting large parts, even if that would be appropriate). - Allocate and free several times the same memory size. - The type that you want to use the dispenser is very small (for example, built-in types such as char, short, int, etc.).
Theoretically, an implementation can use pool_ memory, which allocates a multiple of the actual size of the distribution, each time it needs to do this (from the main memory manager). Objects that are close to each other are more suitable for any cache and / or prefetch algorithm. I implemented such a memory pool with some overhead to handle the correct allocation, separation and deallocation (we cannot free every address that the user skips to free. We only need to free the addresses that are the beginning of each memory block that we have previously highlighted).
I tested both cases with the following really simple code:
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 calls allocactor::allocate(1, 0) every time push_back is called. unique() ensures that every element is affected and comparable to the next element. However, the result was disappointing. The minimum overhead required to manage a pool of blocks is more than any possible advantage of the system.
Can you think of a scenario in which this will improve performance?
EDIT: Of course, this is much faster than std::allocator .