同じことを知りたくて測定しました。私のボックス(AMD FX(tm)-8150 Eight-Core Processor at 3.612361 GHz)では、独自のキャッシュラインにあり、すでにキャッシュされているロック解除されたミューテックスをロックおよびロック解除すると、47クロック(13 ns)かかります。
2つのコア間の同期(CPU#0と#1を使用)のため、2つのスレッドで102 nsごとに1回しかロック/ロック解除ペアを呼び出せなかったため、51 nsごとに1回しか呼び出せず、およそ38 nsは、スレッドがロック解除を行った後、次のスレッドが再びロックする前に回復します。
これを調査するために使用したプログラムは、次の場所にあります。
https //github.com/CarloWood/ai-statefultask-testsuite/blob/b69b112e2e91d35b56a39f41809d3e3de2f9e4b8/src/mutex_test.cxxにあります。
ボックスに固有のハードコードされた値(xrange、yrange、rdtscオーバーヘッド)がいくつかあることに注意してください。そのため、機能する前に、実験する必要があるでしょう。
その状態で生成されるグラフは次のとおりです。
これは、次のコードでベンチマークを実行した結果を示しています。
uint64_t do_Ndec(int thread, int loop_count)
{
uint64_t start;
uint64_t end;
int __d0;
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (start) : : "%rdx");
mutex.lock();
mutex.unlock();
asm volatile ("rdtsc\n\tshl $32, %%rdx\n\tor %%rdx, %0" : "=a" (end) : : "%rdx");
asm volatile ("\n1:\n\tdecl %%ecx\n\tjnz 1b" : "=c" (__d0) : "c" (loop_count - thread) : "cc");
return end - start;
}
2つのrdtsc呼び出しは、「ミューテックス」をロックおよびロック解除するために必要なクロック数を測定します(私のボックスのrdtsc呼び出しには39クロックのオーバーヘッドがあります)。3番目のasmは遅延ループです。遅延ループのサイズは、スレッド0の場合よりスレッド1の場合のほうが1カウント小さいため、スレッド1の方がわずかに高速です。
上記の関数は、サイズが100,000のタイトループで呼び出されます。スレッド1の方が関数は少し高速ですが、ミューテックスの呼び出しのため、両方のループが同期します。これは、ロック/ロック解除のペアで測定されたクロック数がスレッド1の方がわずかに多いため、その下のループの遅延が短いことを考慮して、グラフで確認できます。
上記のグラフでは、右下のポイントは遅延loop_countが150の測定値であり、次に下のポイントを左に向かって、loop_countは測定ごとに1つずつ減少します。77になると、両方のスレッドで102 nsごとに関数が呼び出されます。その後、loop_countがさらに減少すると、スレッドを同期することができなくなり、ほとんどの場合、mutexが実際にロックされ始め、ロック/ロック解除に必要なクロック量が増加します。このため、関数呼び出しの平均時間も増加します。プロットポイントが上昇し、右方向に戻ります。
これから、50 nsごとのミューテックスのロックとロック解除は私のボックスでは問題ではないと結論付けることができます。
結局のところ、私の結論は、OPの質問に対する答えは、ミューテックスを追加することが競合が少なくなる限り、より良いということです。
mutexはできるだけ短くロックしてください。それらをループの外に置く唯一の理由は、そのループが100 ns(つまり、そのループを同時に50 nsで実行したいスレッドの数)または13 nsの時間よりも速くループする場合です。ループサイズは、競合によって発生する遅延よりも遅延が大きくなります。
編集:私は今この主題についてもっと知識を得て、ここで提示した結論を疑い始めました。まず第一に、CPU 0と1はハイパースレッド化されています。AMDは8つの実際のコアがあると主張していますが、他の2つのコア間の遅延がはるかに大きいため(つまり、2と3、4と5、6と7のように、0と1はペアを形成するため) )。次に、std :: mutexは、mutexのロックをすぐに取得できない場合にシステムコールを実際に実行する前に少しロックをスピンするように実装されています(間違いなく非常に遅くなります)。したがって、ここで測定したのは、最も理想的な状況であり、実際のロックとロック解除は、ロック/ロック解除ごとに大幅に時間がかかる可能性があります。
結論として、ミューテックスはアトミックで実装されています。コア間でアトミックを同期するには、内部バスをロックする必要があります。これにより、対応するキャッシュラインが数百クロックサイクルの間フリーズします。ロックを取得できない場合は、システムコールを実行してスレッドをスリープ状態にする必要があります。これは明らかに非常に低速です(システムコールは10ミリ秒のオーダーです)。スレッドはとにかくスリープしなければならないので、通常、それは実際には問題ではありませんが、スレッドが通常スピンしている間ロックを取得できず、システムコールを実行できない高競合の問題である可能性があります。その後すぐにロックをかけてください。たとえば、複数のスレッドがタイトなループでミューテックスをロックおよびロック解除し、それぞれが1マイクロ秒程度ロックを保持する場合、次に、彼らは常にスリープ状態に置かれ、再び目覚めるという事実によって、非常に遅くなる可能性があります。また、スレッドがスリープ状態になり、別のスレッドがウェイクアップする必要がある場合、そのスレッドはシステムコールを実行する必要があり、約10マイクロ秒遅延します。したがって、この遅延は、(スピンに時間がかかりすぎた後)別のスレッドがカーネルでそのミューテックスを待機しているときにミューテックスをロック解除しているときに発生します。