3

I am working on a concurrent data structure that uses a number of small sized temporary objects. A lot of these objects are the same size. So to reduce the strain on the memory allocator I have been using a thread local map to store the objects as <size, list> tuples.

When a thread needs an object, it checks the map for a suitable object before going to the allocator. This works well and has improved performance significantly, however it is prone to the issue where overtime one thread loses its entire pool to other threads, forcing it to allocate new objects. If the application runs for a long period of time, I find a few threads having memory pools way to large.

To solve this I want to add a shared memory pool between the thread local pools and the allocator. Since the number of structs and the size of the structs are constant at compile time, I figure that there should be someway using macros to map each size to an array position. Allowing for a much lighter memory management.

Here is my current solution

#define RC_OBJECT_COUNT 0
#define ADD_RC_OBJECT(object) \
    #ifndef RC_MAP_``sizeof(object)''
        #define RC_OBJECT_TEMP RC_OBJECT_COUNT \
        #undefine RC_OBJECT_COUNT \
        #define RC_OBJECT_COUNT RC_OBJECT_TEMP+1 \
        #define RC_MAP_``sizeof(object)'' RC_OBJECT_TEMP
    #endif

Is there a way to echo the the result of the call to sizeof(object) into the defined variable name? Preferably without a separate configuration script.

4

1 に答える 1

0

私はあなたが議論しているように動作するコードを書きましたが、あなたがやろうとしている方法でプリプロセッサマクロをまったく使用しませんでした.

私のコードには、小さな API を備えた単一の「オブジェクト マネージャー」があります。オブジェクトを取得したいプログラムのどの部分でも、「次の特性を持つオブジェクトを要求したい」という API 関数、「登録関数」を呼び出します。register 関数はハンドルを返します。次に、GetObject()ハンドルを引数として取り、オブジェクトへのポインターを返す関数があります。コードがオブジェクトで完了すると、オブジェクトReleaseObject()へのポインタを取る関数があります。

異なるオブジェクトごとに、「準備完了リスト」と呼ばれるリンクされたリストがあります。このコードは、常にリストの先頭に挿入および削除します (初期化されていないオブジェクトは別のオブジェクトと同等であり、それらはすべて同じサイズであるため)。私のコードはシングルスレッドなので、ロックの問題はありませんが、マルチスレッドの場合は、各準備リストにロックを設定する必要があります。(ただし、リンクされたリストの先頭で挿入または削除するのは非常に高速であるため、スレッドが非常に長いロックを必要とすることはありません。)

私の目的のために、プログラムのさまざまな部分でオブジェクトを共有できるので、各オブジェクトの参照カウントがありました。 ReleaseObject()参照カウントをデクリメントし、それがゼロになると、オブジェクトを適切な準備完了リストに入れます。

によって返されるハンドルはGetObject()、リンクされたリスト構造へのポインターにすぎません。

私のコードでは、 への呼び出しがGetObject()あり、準備完了リストが空の場合、malloc()が自動的に呼び出され、新しいオブジェクトが作成されて返されます。register 関数は、 でオブジェクトを作成するために呼び出す関数へmalloc()のポインタ、 でオブジェクトを解放するための関数へfree()のポインタ、および「サニティ チェック」関数へのポインタを受け取ります (私は自分のプログラムが で実行時に自分自身をチェックするのが好きなので)への呼び出しassert())、およびオブジェクトのサイズなどの引数。

プログラムの複数の部分で同じ種類のオブジェクトが必要であると登録されている場合、オブジェクト マネージャーはこれに気づき、登録の最初の呼び出しによって既に設定されている準備完了リストへのハンドルを返します。現在、それらは単一の準備完了リストでオブジェクトを共有しています。

これは複雑に聞こえるかもしれませんが、ビルドにそれほど時間はかからず、非常にうまく機能します。準備完了リストにオブジェクトがない場合、オブジェクト マネージャーは、準備完了リスト構造体に格納されている関数ポインターを呼び出して新しいオブジェクトを取得し、それを返します。

プログラムで見つけた最も一般的なバグはReleaseObject()、オブジェクトの処理が完了したときに呼び出しに失敗することです。次に、プログラムにメモリ リークが発生しmalloc()、多くの呼び出しが行われ、組み込みプラットフォームではメモリが不足して停止します。通常、これに気づき、適切な呼び出しを追加するのは非常に簡単ReleaseObject()です。

編集: (コメントの質問への回答) オブジェクト マネージャーは、さまざまなオブジェクト管理構造体インスタンスの配列を保持します。各構造体には、3 つの関数ポインターが格納されます。「新しい」関数へのポインター、「削除」関数へのポインター、「サニティ チェック」関数へのポインター。「new」関数が呼び出されたときに渡されるいくつかの値 (たとえば、目的のバッファーのサイズ)。オブジェクトのリンクされたリストの先頭。コードが "register" 関数を呼び出すと、オブジェクト マネージャーは、この配列内に "register" からの同一の値 (3 つの関数ポインターといくつかの値) があるかどうかを確認します。同一の値が見つかった場合、オブジェクト マネージャーはそのオブジェクト マネージャー構造体インスタンスへのポインターを返します。同一の値が見つからない場合は、

これは、私の「登録」関数は、管理されているさまざまな種類のオブジェクトの数が O(N) であることを意味しますが、私のアプリには約 4 種類のオブジェクトしかないため、これを最適化しようとはしませんでした。私の "get" 関数は O(1) です。これは、正しいオブジェクト マネージャー構造体へのポインターがあり、リンク リストの先頭からの削除は一定時間の操作であるためです。

オブジェクト マネージャー構造体の配列はによって割り当てられmalloc()、追加のオブジェクト型が登録されている場合は、コードを呼び出しrealloc()てメモリを増やすことができます。

私のアプリケーションでは、「登録解除」操作は必要ありませんでしたが、登録解除操作が必要な場合は、準備完了リストのすべてのオブジェクトを解放し、オブジェクト マネージャー配列内のその場所を未使用としてマークする必要があります。

私のアプリはオーディオ処理エンジンであり、オーディオの処理中に呼び出すことは決してありません。これは、フリー ブロック リストなどを再編成することを決定してしばらく時間がかかり、オーディオの再生に不具合がmalloc()生じる可能性があるためです。malloc()エンジンには、再生を開始する前に「init」フェーズがあり、コードが「register」関数を呼び出し、すべてのオーディオ バッファが割り当てられます。その後、実行時にバッファが飛んで準備完了リストに追加されます。これは本当にうまく機能し、低電力の DSP チップで問題なく動作しました。

于 2013-10-29T19:27:21.260 に答える