boost memory pool

pool

pool is the simplest and easiest to use memory pool class, which can return a memory pointer of a simple data type ((POD)R. It is located in the namespace boost and needs to include the header file <boost/pool/pool.hpp>, namely:

#include <boost/pool/pool.hpp>

using namespace boost;

template<typename UserAllocator=default_user_allocator_new_delete>
class pool
{
    public:
    explicit pool(size_type requested_size);
    ~pool();
    size_type get_requested_size() const;//allocated memory block size
    void*  malloc();
    void*  ordered_malloc();
    void*  ordered_malloc(size_type n);
    bool   is_from(void* chunk) const;
    ....
}

The template type parameter UserAllocator of the pool is a user-defined memory allocator, which implements a specific memory allocation algorithm. Usually, the default default_user_allocator_new_delete can be used directly, which internally uses new[] and delete[] to allocate memory.

The constructor of pool accepts an integer requested_size of type size_type, indicating the size of each allocated memory block (instead of the size of the memory pool), which can be obtained with get_requested_size(). The pool will automatically apply for or return the used memory to the system as needed, and when it is destructed, the pool will automatically release all the memory it holds.

The member functions malloc() and ordered_malloc() behave very similar to the global function malloc() in c, using a void* pointer to return a memory block allocated from the memory pool, the size of which is the requested_size specified in the constructor. If the memory allocation fails, the function will return p (ie, a null pointer) and will not throw an exception. malloc( ) allocates a block of memory arbitrarily from the memory pool, while ordered_malloc() merges the free block list while allocating. The parameterized form of ordered_malloc() can also allocate n blocks of memory contiguously. The allocated memory block can be tested with the is_from() function to see if it is allocated from this memory pool.

There are two last member functions: release_memory () allows the memory pool to release all unallocated memory, but the allocated memory blocks are not affected; Purge_ Memor () forces the release of all memory held by the pool, regardless of whether the memory block is used or not. In fact, the destructor of the pool is the call to purge_memory (). These two functions should not be called manually by the programmer in general.

Example:

#include<boost/pool/pool.hpp>
#include<iostream>
using namespace boost;
int main()
{
    pool<>p1(sizeof(int));//A pool of int s that can be allocated
    int* p=static_cast<int*>(p1.malloc());
    if(p!=nullptr)
    {
        std::cout<<"yes"<<std::endl;
    }
    p1.free(p);
    for(int i=0;i<100;i++)
    {
        p1.ordered_malloc(10);
    }
}


object_pool

object_pool is a memory pool for class instances (objects), its function is similar to pool, but it will call the destructor on all allocated memory blocks when it is destructed, so as to release resources correctly.

object_pool is located in the namespace boost and needs to include the header file <boost/pool/object_pool.hpp>, namely:

#include <boost/pool / object_pool.hpp>

using namespace boost;

Class summary:

template<typename T,typename UserAllocator>
class object_pool: protected poll<UserAllocator>
{
 public:
 using Element_type=T;
 public:
 object_pool();
 ~object_pool();

 Element_type* malloc();
 void          free(Element_type* p);
 bool          is_from(Element_type* p)const;

 Element_type construct();
 void destroy(Element_type* p);
};

object pool is a subclass of pool, but it uses protected inheritance, so it cannot use the interface of pool, but the basic operations are still very similar

The template type parameter T of the object pool specifies the element type to be allocated by the object_pool, and its destructor function is required not to throw an exception. Once the type is specified in the template, the object_pool instance can no longer be used to allocate objects of other types.

What is special about object_pool is the construct() and destroy() functions, which are the real value of object_pool.

construct() is actually a set of functions, overloaded with multiple parameters (currently supports up to 3 parameters, but can be extended), it first calls malloc() to allocate memory, and then uses the passed in memory block The parameter calls the constructor of the class, and returns an initialized object pointer. destroy() calls the object's destructor first, and then uses free() to free the memory block.

When compiling, add the external library -lboost_system;

#include<boost/pool/object_pool.hpp>
#include<iostream>
#include<string>
using namespace boost;

struct demo_class
{
    public:
    int a,b,c;
    demo_class(int x=1,int y=2,int z=3):
    a(x),b(y),c(z){}

};
int main()
{
    object_pool<demo_class>p1;
    auto p=p1.malloc();
    assert(p1.is_from(p));
    
    p=p1.construct(6,7,8);
    assert(p->a==6);

    object_pool<std::string>pls;
    for(int i=0;i<10;i++)
    {
        std::string *ps=pls.construct("hello object_pool");
        std::cout<<*ps<<std::endl;
    }


}
//
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool
hello object_pool

More build parameters:

By default, when using construct() of object _pool, we can only use up to 3 parameters to create an object. This is sufficient in most cases, but sometimes we may define a constructor with more than 3 parameters, in which case the default overloaded form of construct() cannot be used.

It's a pity that construct() didn't follow the c++ standard in time to support any number of argument constructions using variadic templates. It implements a workaround extension mechanism based on the macro preprocessing m4 (usually comes with UNIX systems, but also has windows version), which can generate construct () function code that accepts any number of parameters.

The pool library provides a script named pool_construct.m4 and pool_construct_simple.m4 under the directory /boost/pool/detail, and also provides sh and bat executable script files of the same name that can run under UNIX and windows. Simply pass an integer parameter N to the batch script, and m4 will automatically generate the source code for the construct() function with N parameters.

./ pool_construct_simple.sh 5; ./pool_construct.sh 5

m4's solution is rather clumsy. Using the variadic template feature, we can define an auxiliary template function that supports any number of parameters to completely solve this problem:

template<typename p,typename ... Args>
inline typename P::element_type*
construct(P&p,Args&&... args)
{
    using P::element_type* mem=p.malloc();
    assert(mem!=0);
    new(mem) typename P::element_type(
        std::forward<Args>(args)...
    );
    return mem;
};

The free function construct() accepts any number of parameters, the first is the object_pool object, followed by the parameters required to create the object. The type of the object to be created can be obtained by using the internal type definition element_type of object_pool. The function first calls malloc() to allocate a block of memory, and then calls the less common " "location new expression" (placement new expression) Create objects.

singleton_pool

singleton_pool has exactly the same interface as pool and can allocate memory pointers of simple data types (POD s), but it is a singleton.

singleton_pool is located in the namespace boost and needs to include the header file <boost/pool/singleton,pool.hpp>, ie;

#include <boost/pool/singleton_pooi .hpp>

using namespace boost;

singleton_pool uses boost.thread library to provide thread safety guarantee by default, so it needs to link boost_thread library,

If you do not use multithreading, you can define the macro BO0ST_POOL_NO_MT before the header file.

Class summary:

template<typename Tag,unsigned RequestedSize>
class singleton_pool
{
    public:
    static bool        is_from(void* ptr);
    void*  malloc();
    void*  ordered_malloc();
    void*  ordered_malloc(size_type n);
    ...
}

singleton_pool mainly has two template type parameters (the rest can use default values). The first Tag is only used to mark different singletons, it can be an empty class or even just a declaration. The second parameter RequestedSize is equivalent to the integer requested_size in the pool constructor, indicating the size of the memory block allocated by the pool.

The interface of singleton_pool is exactly the same as that of pool, but the member functions are static, so there is no need to declare an instance of singleton_pool, and the domain operator "::" is used to call static member functions directly. Because singleton_pool is a singleton, its life cycle is as long as the entire program. Unless release_memory() or purge_memory() is manually called, singleton_pool will not automatically release the occupied memory. Except for these two points, the usage of singleton pool is exactly the same as pool.

usage:

#include<boost/pool/singleton_pool.hpp>
#define BOOST_POLL_NO_MT
using namespace boost;
#include<assert.h>

struct pool_tag{};
using sql=singleton_pool<pool_tag,sizeof(int)>;
int main()
{
    int *p=(int*)sql::malloc();
    assert(sql::is_from(p));
    sql::release_memory();
}

Tags: C++ boost programming language

Posted by intech on Tue, 13 Sep 2022 21:50:33 +0530