Tiny (ANSI) C library for pool allocation.
This library implements a very simple pool allocator in ANSI C. This library was originally inspired by Dmitry Soshnikov’s article.
Below is a simple explanation of how the allocator works, but more details about a sample implementation can be found in the article on my blog.
Similarly to malloc, a pool allocator allows the user to allocate memory at run
time. The pool allocator, however, is much faster than malloc, at the cost of
having a fixed pool size. It allows the user to allocate and free memory blocks
(referred to as chunks, from now on) in O(1) linear time.
The code of the allocator is heavily commented, so it should be easy to understand how everything works, and why it is so efficient.
This library uses very little memory: When calling pool_new (described below), a
very small Pool structure is allocated, along with the pool itself. Free chunks
are used to store information, so the memory impact is minimal.
The library doesn’t have any dependencies, not even to the standard C library
(as long as it’s compiled with LIBPOOL_NO_STDLIB defined). For more information,
see the Usage section below.
If you want to use this library, simply copy the detour source and headers to
your project, include the libpool.h header in your source files and compile the
libpool.c source along with the rest of your code.
The library doesn’t depend on any specific allocation method; it uses two
function pointers, pool_ext_alloc and pool_ext_free (which by default point to
malloc and free) for allocating the Pool structures and the pools
themselves. The libpool.c source can be compiled with LIBPOOL_NO_STDLIB defined,
and these pointers won’t be initialized. It’s up to the user to specify a valid
function for allocating and freeing memory.
This is the basic process for using this allocator, using the functions described below:
- Create a new pool, specifying the number of elements and the size of each element.
- Allocate necessary chunks from the pool, and use them.
- When a chunk is no longer needed, free it so it can be used by subsequent allocations.
- Optionally expand the pool, if you run out of chunks.
- When the pool itself is no longer needed, destroy it.
For a full example, see src/libpool-test.c.
This library consists of only 5 functions:
- Function:
pool_new -
Allocate and initialize a new
Poolstructure, with the specified number of chunks, each with the specified size. If the initialization fails,NULLis returned.This function will allocate a
Poolstructure, along with the array of chunks used for later allocations. The caller must free the returned pointer usingpool_destroy.Note that the
chunk_szargument must be greater or equal thansizeof(void*). For more information, see the Caveats section. - Function:
pool_expand -
Expand the specified
pool, addingextra_szfree chunks.On success, it returns true; otherwise, it returns false and leaves the pool unchanged.
- Function:
pool_destroy -
Free all data in a
Poolstructure, along with the structure itself. After a pool is destroyed, all data previously allocated from that pool (withpool_alloc) becomes unusable.Allows
NULLas the argument. - Function:
pool_alloc -
Allocate a fixed-size chunk from the specified pool. If no chunks are available,
NULLis returned. - Function:
pool_free -
Free a fixed-size chunk from the specified pool. Allows
NULLas bothpoolandptrarguments.
This library has support for the valgrind framework, unless it has been compiled
with the LIBPOOL_NO_VALGRIND macro defined. Among other things, this allows the
programmer to check for memory leaks and invalid memory accesses in his program
with the valgrind tool, just like it would with a program that uses malloc. Note
that the valgrind macros don’t affect the behavior of the program when it’s not
running under valgrind.
For example, after compiling the test program with valgrind disabled (i.e. with
LIBPOOL_NO_VALGRIND defined), if we run valgrind ./libpool-test.out we will see
9 allocations, corresponding to the malloc calls. However, if we enable valgrind
support, we will see 111 allocations because it also checked the calls to
pool_alloc and pool_free.
You might need to install some valgrind-devel package in your system in order to
include the necessary headers.
By default, when initializing a new pool with pool_new, the chunk sizes will be
internally aligned to the size of a void*, so that all pointers returned by
pool_alloc are also aligned.
In some specific cases, this can lead to unexpected pool sizes, so this behavior
can be disabled at compile-time by defining LIBPOOL_NO_ALIGNMENT.
However, note that, when compiling the library with alignment disabled, the
pool_new function will expect a chunk size greater or equal to the size of a
void*. This is necessary because the implementation uses free chunks to build a
linked list, which is what makes the library so efficient. If the chunk_sz
parameter of pool_new is smaller than sizeof(void*), the function will return
NULL.
Clone the repository and build the project using make.
git clone https://github.com/8dcc/libpool
cd libpool
make
# ...Then, run libpool-test.out.
./libpool-test.out
# ...You can benchmark the library by running make benchmark.
make benchmark
# ...
# Benchmarking [10000..100000] allocations of 10000 bytes. Ignoring first 100000 calls.
# ...
# Logging finished, plotting to 'benchmark.svg'...
# All doneThese are the results in my machine:
Without using a logarithmic scale in the Y axis:
You can adjust these values in the benchmark.sh script.