0

私はCでクロスプラットフォームの共有ライブラリを書いています。このライブラリのワークフローは次のようになります。

lib *handle = lib_init();
result = lib_do_work(handle);
lib_destroy(handle);

通常、ユーザーはアプリケーションが開始すると開始し、アプリケーションが終了すると閉じます。lib_do_work()通常、1秒間に複数回呼び出されます。したがって、各呼び出しのメモリ割り当てと割り当て解除を回避するために、プーリングメカニズムを使用しています。これで、必要な構造のインスタンスを返すようにプールに依頼します。プールは未使用のインスタンスを返すか、空きがない場合は新しいインスタンスを作成します。この新しいインスタンスもプールに追加され、次回使用できるようになります。

reset_pool()ライブラリへのAPI呼び出しはすべて、プール内のすべての要素を再び使用できるようにする関数呼び出しで始まります。このプールは、呼び出しの一部として破棄されlib_destroy()ます。私のテストでは、プールが構造体インスタンスの100000以上のインスタンスを取得していることを確認しました。

これはメモリを処理する上で良い習慣ですか?どんな助けでも素晴らしいでしょう。

4

2 に答える 2

1

コメントですでに指摘されているように、割り当てと割り当て解除がアプリケーションのボトルネックであるかどうかは、プロファイリングだけでわかります。また、システムが常に同じサイズのオブジェクトのみを割り当ておよび割り当て解除する場合、デフォルトの実装はおそらくかなりうまく機能します。

通常、プールは、ブロックまたは要素を事前に割り当てることにより、割り当ての最適化を提供します。ブロックは、個々の割り当て要求を満たすために個々の要素に分割されます。プールが使い果たされると、新しいブロックが割り当てられます。これは、ライブラリ アロケータへの呼び出しを少なくする方がコストがかからないため、割り当ての最適化です。

プール アロケーターは、断片化を減らすのにも役立ちます。アプリケーションがさまざまなサイズのオブジェクトをさまざまな有効期間で割り当ておよび割り当て解除すると、断片化の可能性が高くなります (そして、デフォルト アロケーターの合体コードはより多くの作業を行う必要があります)。異なるサイズのオブジェクトごとにプール アロケータが作成され、各プール ブロックが同じサイズである場合、これにより断片化が効果的に解消されます。

(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);
}
于 2012-07-10T07:47:26.890 に答える
1

現在のアーキテクチャにとって複雑すぎるかどうかはわかりませんが、通常、プールはプールされたインスタンスの数を制限し、プールされたすべてのインスタンスがビジー状態のときにリクエストをキューに入れます。

于 2012-07-10T07:49:38.093 に答える