How to use boost::pool library to create a custom memory allocator

前端 未结 1 1242
孤街浪徒
孤街浪徒 2021-02-03 13:23

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 vect

1条回答
  •  梦谈多话
    2021-02-03 14:22

    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 structure B 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;
    
    template  struct my_pool_alloc {
        using value_type = T;
    
        my_pool_alloc(Pool& pool) : _pool(pool) {
            assert(pool_size() >= sizeof(T));
        }
    
        template 
        my_pool_alloc(my_pool_alloc const& other) : _pool(other._pool) {
            assert(pool_size() >= sizeof(T));
        }
    
        T *allocate(const size_t n) {
            T* ret = static_cast(_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  bool operator==(const my_pool_alloc &a, const my_pool_alloc &b) { return a.pool_size()==b.pool_size(); }
    template  bool operator!=(const my_pool_alloc &a, const my_pool_alloc &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 > v(1024, pool);
            v.resize(20480);
        };
    
        // pool.release_memory();
    
        {
            vector > v(1024, pool);
            v.resize(20480);
        }
    
        // pool.release_memory();
    
        // sharing the pool between multiple live containers
        {
            vector > v(512, pool);
            vector > 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

    0 讨论(0)
提交回复
热议问题