As has already been pointed out in the comments, only profiling will tell you if allocations and deallocations are a bottleneck in your application. Also, if your system only allocates and deallocates the same sized object all the time, then the default implementation will probably perform pretty well.
Usually, a pool provides allocation optimization by pre-allocating a block or elements. The block is carved out into individual elements to satisfy individual allocation requests. When the pool is depleted, a new block is allocated. This is an allocation optimization since it is cheaper to make fewer calls to the library allocator.
A pool allocator can also help to reduce fragmentation. If the application allocates and deallocates different sized objects with varying lifetimes, then the chance for fragmentation increases (and the coalescing code in the default allocator has to do more work). If a pool allocator is created for each different sized object, and each pool block was the same size, this would effectively eliminate fragmentation.
(As Felice points out, there is another kind of pool that pre-allocates a fixed amount of memory for the application to use, as a way to ensure the application does not use more memory than it is provisioned.)
On deallocation, individual elements can be placed onto a freelist. But. your reset_pool
implementation can just walk through the blocks, free each one, and then allocate a new block.
The following is kind of simplistic. It only deals with one kind of element. POOL_SIZE would need to be tuned to be something reasonable for your application. Assume data structures like this:
typedef struct element {
struct element *next;
/* ... */
} element;
typedef struct pool_block {
struct pool_block *next;
struct element block[POOL_SIZE];
} pool_block;
typedef struct element_pool {
struct pool_block *pools;
struct element *freelist;
int i;
} element_pool;
Then, the API would look something like:
void pool_init (element_pool *p) { /* ... */ }
element * pool_alloc (element_pool *p) {
element *e = p->freelist;
if (e) p->freelist = e->next;
else do {
if (p->i < POOL_SIZE) {
e = &p->pools->block[p->i];
p->i += 1;
} else {
pool_block *b = pool_block_create();
b->next = p->pools;
p->pools = b;
p->i = 0;
}
} while (e == 0);
return e;
}
element * pool_dealloc (element_pool *p, element *e) {
e->next = p->freelist;
p->freelist = e;
}
void pool_reset (element_pool *p) {
pool_block *b;
while ((b = p->pools)) {
p->pools = b->next;
pool_block_destroy(b);
}
pool_init(p);
}