コメントですでに指摘されているように、割り当てと割り当て解除がアプリケーションのボトルネックであるかどうかは、プロファイリングだけでわかります。また、システムが常に同じサイズのオブジェクトのみを割り当ておよび割り当て解除する場合、デフォルトの実装はおそらくかなりうまく機能します。
通常、プールは、ブロックまたは要素を事前に割り当てることにより、割り当ての最適化を提供します。ブロックは、個々の割り当て要求を満たすために個々の要素に分割されます。プールが使い果たされると、新しいブロックが割り当てられます。これは、ライブラリ アロケータへの呼び出しを少なくする方がコストがかからないため、割り当ての最適化です。
プール アロケーターは、断片化を減らすのにも役立ちます。アプリケーションがさまざまなサイズのオブジェクトをさまざまな有効期間で割り当ておよび割り当て解除すると、断片化の可能性が高くなります (そして、デフォルト アロケーターの合体コードはより多くの作業を行う必要があります)。異なるサイズのオブジェクトごとにプール アロケータが作成され、各プール ブロックが同じサイズである場合、これにより断片化が効果的に解消されます。
(Felice が指摘しているように、アプリケーションがプロビジョニングされた以上のメモリを使用しないようにする方法として、アプリケーションが使用する一定量のメモリを事前に割り当てる別の種類のプールがあります。)
割り当てを解除すると、個々の要素をフリーリストに配置できます。しかし。reset_pool
実装では、ブロックをウォークスルーし、それぞれを解放してから、新しいブロックを割り当てることができます。
以下はやや単純化したものです。1 種類の要素のみを扱います。POOL_SIZE は、アプリケーションにとって妥当なものになるように調整する必要があります。次のようなデータ構造を想定します。
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;
次に、API は次のようになります。
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);
}