あなたの干渉アプリが新規/削除(malloc/free)を使用していた場合、干渉アプリは非リサイクルテストにさらに干渉するようです。しかし、干渉テストがどのように実装されているかわかりません。
リサイクル方法によっては (つまり、禁止されている pthread ミューテックスを使用している場合)、リサイクル コードが遅くなる可能性があります (gcc アトミック ops は、リサイクルの実装で 40 倍高速になります)。
Malloc は、少なくとも一部のプラットフォームで長い間何らかのバリエーションを持っていましたが、スレッドを認識していました。 gcc でコンパイラ スイッチを使用して、確実に取得してください。新しいアルゴリズムは、各の小さなメモリ チャンクのプールを維持します。 スレッドに小さなアイテムがある場合、ブロッキングはまったくまたはほとんどありません。これは単純化しすぎており、システムが使用している malloc によって異なります。さらに、テストを行うために数百万のアイテムを割り当てても、小さなアイテム プールのサイズは限られているため、その効果は見られません。または多分あなたはそうするでしょう。知らない。割り当てた直後にアイテムを解放すると、表示される可能性が高くなります。解放された小さなアイテムは、共有ヒープではなく、小さなアイテム リストに戻されます。 「スレッドBがスレッドAによって割り当てられたアイテムを解放するとどうなるか」は、mallocのバージョンで処理される場合と処理されない場合がある問題であり、非ブロック的な方法で処理されない場合があります。確かに、大規模なテスト中にすぐに解放しなかった場合、スレッドは小さな項目リストを何度も再入力する必要があります。複数のスレッドが試行すると、ブロックされる可能性があります。最後に、ある時点で、プロセスのヒープがシステムにヒープ メモリを要求しますが、これは明らかにブロックされる可能性があります。
では、小さなメモリーアイテムを使用していますか?あなたのmallocの場合、何が小さいかわかりませんが、1k未満の場合は確かに小さいです。割り当てと解放を交互に行っていますか、それとも何千ものノードを割り当ててから何千ものノードを解放していますか?干渉アプリが割り当てていましたか?これらすべてが結果に影響します。
アトミック オペレーション (CAS =比較と交換) でリサイクルする方法:
まず、ノード オブジェクトに pNextFreeNode を追加します。私は void* を使用しました。あなたのタイプを使用できます。このコードは 32 ビット ポインター用ですが、64 ビットでも機能します。次に、グローバル リサイクル パイルを作成します。
void *_pRecycleHead; // global head of recycle list.
リサイクルの山に追加:
void *Old;
while (1) { // concurrency loop
Old = _pRecycleHead; // copy the state of the world. We operate on the copy
pFreedNode->pNextFreeNode = Old; // chain the new node to the current head of recycled items
if (CAS(&_pRecycleHead, Old, pFreedNode)) // switch head of recycled items to new node
break; // success
}
山から取り除く:
void *Old;
while (Old = _pRecycleHead) { // concurrency loop, only look for recycled items if the head aint null
if (CAS(&_pRecycleHead, Old, Old->pNextFreeNode)) // switch head to head->next.
break; // success
}
pNodeYoucanUseNow = Old;
CAS を使用すると、変更するアイテムが渡した古い値である場合にのみ操作が成功することを意味します。競合があり、別のスレッドが最初にそこに到達した場合、古い値は異なります。実生活では、このレースはめったに起こりません。 CAS は、実際に値を設定するよりもわずかに遅いだけなので、ミューテックスと比較して....それは揺るぎません.
上記の削除は、同じアイテムをすばやく追加および削除すると競合状態になります。 CAS'able データにバージョン # を追加することで解決します。リサイクルパイルの先頭へのポインターと同時にバージョン # を実行すると、あなたの勝ちです。ユニオンを使用してください。 CAS 64 ビットに余分な費用はかかりません。
union TRecycle {
struct {
int iVersion;
void *pRecycleHead;
} ; // we can set these. Note, i didn't name this struct. You may have to if you want ANSI
unsigned long long n64; // we cas this
}
64ビットOSの場合、128ビット構造体に移動する必要があることに注意してください。そのため、グローバル リサイクル パイルは次のようになります:
TRecycle _RecycleHead;
リサイクルの山に追加:
while (1) { // concurrency loop
TRecycle New,Old;
Old.n64 = _RecycleHead.n64; // copy state
New.n64 = Old.n64; // new state starts as a copy
pFreedNode->pNextFreeNode = Old.pRecycleHead; // link item to be recycled into recycle pile
New.pRecycleHead = pFreedNode; // make the new state
New.iVersion++; // adding item to list increments the version.
if (CAS(&_RecycleHead.n64, Old.n64, New.n64)) // now if version changed...we fail
break; // success
}
山から取り除く:
while (1) { // concurrency loop
TRecycle New,Old;
Old.n64 = _RecycleHead.n64; // copy state
New.n64 = Old.n64; // new state starts as a copy
New.pRecycleHead = New.pRecycledHead.pNextFreeNode; // new will skip over first item in recycle list so we can have that item.
New.iVersion++; // taking an item off the list increments the version.
if (CAS(&_RecycleHead.n64, Old.n64, New.n64)) // we fail if version is different.
break; // success
}
pNodeYouCanUseNow = Old.pRecycledHead;
この方法でリサイクルすれば、パフォーマンスが向上するはずです。
この質問には多くの適切な回答があります:マルチスレッド C/C++ では、malloc/new はメモリを割り当てるときにヒープをロックしますか?
そこにあるコンセンサスは、ロックがあるということです。そのため、大きな割り当てまたはスワッピングが必要な割り当ては、進行中の大きな割り当てがない場合に小さな割り当てが終了したとしても、別のスレッドで小さな割り当てをブロックする可能性があります。
gcc の new は、pthreads サポートを使用してコンパイルした場合、スレッドセーフですが、それは実際に求めているものではありません。
Windows では、プログラムの開始時にメモリを設定するために使用できる独自のヒープを作成できることを知っています。同様のことを行うための linux/unix 呼び出しについては知りません。
これは、この質問とほとんど同じです。
基本的には malloc
はスレッド セーフとは定義されていませんが、実装者は自由に実装を追加してスレッド セーフにすることができます。あなたの説明から、あなたの特定のバージョンがそうであるように思えます.
確かに、オビ=ワンの言葉を借りれば、「源を使え、ルーク」。 malloc
ソースは出回っており、通常は非常に簡単に読むことができます。
@Mark、標準の GNU libc ソースは
$ git clone git://sourceware.org/git/glibc.git
$ cd glibc
$ git checkout --track -b glibc-2_11-branch origin/release/2.11/master
こちらもご覧ください。 malloc
を覚えておいてください マニュアルのセクション 3 にあります。これはライブラリ関数であるため、カーネル ソースには含まれません。ただし、brk
まで読み込む必要がある場合があります。 ,sbrk
、 getrlimit
と setrlimit
など、カーネルが何をしているかを調べます。
もう 1 つのリンク:GCC プロジェクト。
わかりました、もう 1 つ (いつでも停止できます):ソースをダウンロードできるページは次のとおりです。ファイルを解凍すると、./malloc/malloc.c
にあるはずです。 .
マルチスレッド システムでは、malloc()
と free()
(そして new
/ delete
) 通常、同期プリミティブを使用して、複数のスレッドから安全に呼び出せるようにします。
この同期は、一部のアプリケーションのパフォーマンスにも影響を与えます。特に、高度な並列環境で多くの割り当てと割り当て解除を行うアプリケーションです。より効率的なマルチスレッド メモリ アロケータは活発な研究分野です - jemalloc
を参照してください と tcmalloc