スピンロック、どれくらい便利ですか?
-
12-09-2019 - |
質問
実際にコード内でスピンロックを使用していることにどのくらいの頻度で遭遇しますか?ビジー ループの使用が実際にロックの使用よりもパフォーマンスが優れているという状況に遭遇するのはどのくらい一般的でしょうか?
個人的には、スレッド セーフを必要とする何らかのコードを作成するときは、さまざまな同期プリミティブを使用してベンチマークを行う傾向があります。その限りでは、ロックを使用した方がスピンロックを使用するよりもパフォーマンスが向上するように思えます。実際にロックを保持している時間がどれほど短いかに関係なく、スピンロックを使用するときに受ける競合の量は、ロックを使用するときに受ける競合の量よりもはるかに大きくなります (もちろん、テストはマルチプロセッサ マシンで実行します)。
「低レベル」コードではスピンロックに遭遇する可能性が高いことはわかっていますが、より高レベルのプログラミングでもスピンロックが役立つと思うかどうか知りたいのですが。
解決
それはあなたがやっていることに依存します。一般的なアプリケーションコードでは、スピンロックを避けたいでしょう。
あなたは命令だけのカップルのためのロックを保持します、と待ち時間が重要であり、低レベルのものでは、スピンロックのマットは、ロックよりも優れたソリューションです。しかし、これらの例は、特にC#は一般的に使用されているアプリケーションの種類には、稀である。
他のヒント
C#では、「スピンロックは、」ほとんど常に悪い方のロックを取るよりも、私の経験では、されている - それは、スピンロックがロックをアウトパフォームしますまれです。
。しかし、それは必ずしもそうではありません。 .NET 4は System.Threadingを追加して.SpinLock の構造。これは、ロックが非常に短い時間のために保持されている、と繰り返しつかんされている状況での利点を提供します。並列プログラミングのためのデータ構造上のMSDNドキュメント:
ロックの待ち時間が短くなることが予想されるシナリオでは、スピンロックはロックの他の形態よりも優れたパフォーマンスを提供しています。
スピンロックを使用すると、ツリーをロックするような何かをやっている場合に、他のロック機構をアウトパフォームすることができます - あなただけの時間の非常に、非常に短い期間の間、各ノードのロックを持っている場合、彼らはアウト伝統を行うことができますロック。私は1つの点で、マルチスレッドのシーンの更新とレンダリングエンジンでは、このに走った - 。Monitor.Enterでロックアウトパフォームするとアウトプロファイルスピンロック
は、特に、デバイスドライバと私のリアルタイムの仕事については、私は彼らに公平なビットを使用しました。それは(私はこれをタイミング時に、最後の)ハードウェアに縛らセマフォなどの同期オブジェクトを待っているが、関係なく、割り込みが発生することは実際にかかる時間、少なくとも20マイクロ秒を咀嚼を中断しないことが判明します。 RDTSCのチェックに続いて、メモリマップされたハードウェアレジスタの1つのチェックは、(ノイズにbasiclyダウン)高nannosecondの範囲にある(あなたがマシンをロックアップしないようにタイムアウトを可能にするため)。すべてで多くの時間を取るべきではないハードウェアレベルのハンドシェイクの場合は、スピンロックを打つことは本当に難しいです。
私の2c:更新がいくつかのアクセス基準を満たしている場合、それらは適切なスピンロック候補になります。
- 速い, つまり、スピンロックを取得し、更新を実行し、スピンロックを保持している間にプリエンプトされないように単一スレッド クォンタムでスピンロックを解放する時間があります。
- ローカライズされた 更新するすべてのデータは、できればすでにロードされている 1 つの単一ページ内にある必要があり、スピンロックを保持している間に TLB ミスが発生することは望ましくありません。また、ページ フォールト スワップ読み取りは絶対に望ましくありません。
- 原子 操作を実行するために他のロックは必要ありません。スピンロック下のロックを決して待機しないでください。
降伏する可能性のあるものについては、通知されたロック構造 (イベント、ミューテックス、セマフォなど) を使用する必要があります。
スピンロックの一つのユースケースがあります。あなたは再帰的ロックのサポートを必要としない場合は、スピンロックは、単一のバイトで実現することができる、との競合が非常に低いならば、CPUサイクルの無駄は無視できます。
実用ケースのために、私はしばしば、配列の異なる要素の更新を安全に並列に発生する可能性要素の数千の配列を有します。同時に同じ要素を更新しようとする二つのスレッドの確率は非常に小さい(低いテンション)が、私はすべての要素(私はそれらの多くを持っているつもりです)のための1つのロックを必要としています。これらのケースでは、私は通常、私は、並列に更新だ配列と同じサイズのubytesの配列を割り当て、(Dプログラミング言語の)としてインラインスピンロックを実装する
while(!atomicCasUbyte(spinLocks[i], 0, 1)) {}
myArray[i] = newVal;
atomicSetUbyte(spinLocks[i], 0);
私は、通常のロックを使用していた場合は、一方、Iは、オブジェクトへのポインタの配列を割り当て、次にこの配列の各要素に対してミューテックスオブジェクトを割り当てなければなりません。上述したようなシナリオでは、これは単なる無駄である。
あなたは、パフォーマンス重視のコードをお持ちの場合は、のと、のあなたは、それが現在、の場合よりも速くする必要があると判断しているとのあなたは重要な要因は、ロックスピードであると判断していますスピンロックを試してみると良いでしょう。他の例では、なぜわざわざ?通常のロックを正しく使用する方が簡単です。
以下の点にご注意ください。
ほとんどのミューテックスの実装は、スレッドが実際にスケジュール解除される前に、少しの間スピンします。このため、これらのミューテックスを純粋なスピンロックと比較するのは困難です。
同じスピンロック上で複数のスレッドが「できるだけ速く」回転すると、すべての帯域幅が消費され、プログラムの効率が大幅に低下します。回転ループに noop を追加して、わずかな「スリープ」時間を追加する必要があります。
あなたはめったに何があなたがそれらを避ける必要がある場合は、アプリケーションコードでスピンロックを使用する必要があります。
私は、通常のOS上で実行されているC#コードでスピンロックを使用する理由の事をすることはできません。ビジーロックは、主にアプリケーションレベルでの廃棄物です - スピニングが必要な場合はロックがすぐにコンテキストスイッチが発生します対あなたは、全体のCPUタイムスライスを使用することがあります。
あなたには、いくつかのケースで受けることができますプロセッサ/コアの= NRスレッドのNRを持っていますが、そのレベルでのパフォーマンスの最適化が必要な場合は、あなたの可能性が貧弱な同期プリミティブと組み込みOS上で作業し、次の世代の3Dゲームを作る高性能コードを、OS /ドライバまたはC#を使用していない場合には作成
グラスゴーHaskellのコンパイラのガベージコレクタでPERFのバグの一つは、それは名前を持っていることをとても迷惑である、「<のhref =」http://hackage.haskell.org/trac/ghc/ticket/3553" のrel = "nofollowをnoreferrer">最後のコア減速する」。これは彼らのGCでのスピンロックの彼らの不適切な使用の直接の結果であると、そのスケジューラにLinux上でexcacerbatedされているが、他のプログラムがCPU時間のために競合している時はいつでも実際には、効果が観察されることができます。
効果は、2番目のグラフここで明らかですここだけの最後のコアよりも多くの影響を与えと見ることができますHaskellのプログラムはわずか5のコアを超えてパフォーマンスの低下を見に、。
使用する際は常にこれらの点に留意してください スピンロック:
- ユーザーモードの高速実行。
- 単一プロセス内のスレッド、または共有メモリ内の場合は複数のプロセス内のスレッドを同期します。
- オブジェクトが所有されるまで戻りません。
- 再帰はサポートされません。
- 「待機中」に CPU を 100% 消費します。
私は個人的に、誰かがスピンロックを使用するのが良い考えだと考えただけで、非常に多くのデッドロックを見てきました。
スピンロックを使用するときは十分に注意してください
(これはいくら強調しても足りません)。