I am new to boost and I want to know how exactly the boost::pool libraries can help me in creating a custom memory allocator. And I have two vector of struct objects. First vector is of structure type A, while second vector is of structure type B. How can I reuse the memory allocated to the first vector to the second vector.
Boost Pool is a library that defines a few allocator types.
Obviously, the focus of the library is to provide Pool Allocators.
Pool Allocators shine when you allocate objects of identical size.
Note If your structure
A
and structureB
aren't identical/very similar size you may not like this design assumption.
The allocators provided by the framework work with singleton pools, and they differentiate on the size of your container value_type. That's a bit inflexible if you want to reuse or even share the pool between different value-types. Also, singleton pools can be inflexible and imply thread-safety costs.
So, I wanted to see whether I could whip up the simplest allocator that alleviates some of these issues.
I used the source to boost::pool_alloc
and the cppreference example as inspiration, and then did some testing and memory profiling.
A More Flexible Stateful Allocator
Here's the simplest pool allocator I could think of:
using Pool = boost::pool<boost::default_user_allocator_malloc_free>;
template <typename T> struct my_pool_alloc {
using value_type = T;
my_pool_alloc(Pool& pool) : _pool(pool) {
assert(pool_size() >= sizeof(T));
}
template <typename U>
my_pool_alloc(my_pool_alloc<U> const& other) : _pool(other._pool) {
assert(pool_size() >= sizeof(T));
}
T *allocate(const size_t n) {
T* ret = static_cast<T*>(_pool.ordered_malloc(n));
if (!ret && n) throw std::bad_alloc();
return ret;
}
void deallocate(T* ptr, const size_t n) {
if (ptr && n) _pool.ordered_free(ptr, n);
}
// for comparing
size_t pool_size() const { return _pool.get_requested_size(); }
private:
Pool& _pool;
};
template <class T, class U> bool operator==(const my_pool_alloc<T> &a, const my_pool_alloc<U> &b) { return a.pool_size()==b.pool_size(); }
template <class T, class U> bool operator!=(const my_pool_alloc<T> &a, const my_pool_alloc<U> &b) { return a.pool_size()!=b.pool_size(); }
Notes:
- This allocator is stateful, and thus requires container implementations that allow them (such as Boost Container, Boost MultiIndex). In theory, all C++11-compliant standard libraries should also support them
- The comparisons should guide the containers to swap/copy the allocator or not. This is not an area I've though much about and the chosen approach to mark all pools of different requested-sizes different might be inadequate for some.
Sample, Tests
On my compilers it works for both std::vector
and Boost's vector
:
- Live On Coliru - with GCC std::vector
- Live On Coliru - with GCC boost::container::vector
- Live On Coliru - with Clang std::vector
- Live On Coliru - with Clang boost::container::vector
All runs are leak-free and ubsan/asan clean.
Note how we re-use the same pool with different containers of different struct sizes, and how we can even use it with multiple live containers at a time, provided that the element types fit in the request size (32)
struct A { char data[7]; };
struct B { char data[29]; };
int main() {
//using boost::container::vector;
using std::vector;
Pool pool(32); // 32 should fit both sizeof(A) and sizeof(B)
{
vector<A, my_pool_alloc<A> > v(1024, pool);
v.resize(20480);
};
// pool.release_memory();
{
vector<B, my_pool_alloc<B> > v(1024, pool);
v.resize(20480);
}
// pool.release_memory();
// sharing the pool between multiple live containers
{
vector<A, my_pool_alloc<A> > v(512, pool);
vector<B, my_pool_alloc<B> > w(512, pool);
v.resize(10240);
w.resize(10240);
};
}
Profiling
Using Valgrind's Memory profiler shows, with the release_memory
lines commented out as shown:
When commenting in the release_memory()
calls:
I hope this looks like the thing you wanted.
Further Ideas: Simple Segregated Storage
This allocator uses the existing pool
which delegate back to malloc/free to allocate memory on demand. To use it with a fixed "realm", you might prefer using simple_segregated_storage
directly. This article looks like a good starter https://theboostcpplibraries.com/boost.pool
© 2022 - 2024 — McMap. All rights reserved.