コードで実際にスピンロックを使用している頻度はどれくらいですか。ビジーループの使用が実際にロックの使用よりも優れている状況に遭遇することはどのくらい一般的ですか?
個人的に、スレッドセーフを必要とするある種のコードを書くとき、私はそれを異なる同期プリミティブでベンチマークする傾向があり、それに関しては、ロックを使用するとスピンロックを使用するよりもパフォーマンスが向上するようです。実際にロックを保持している時間がどれだけ短くても、スピンロックを使用したときに受け取る競合の量は、ロックを使用したときに得られる量よりもはるかに多くなります(もちろん、マルチプロセッサマシンでテストを実行します)。
「低レベル」コードでスピンロックに遭遇する可能性が高いことはわかっていますが、それがさらに高レベルの種類のプログラミングで役立つかどうかを知りたいですか?
それはあなたがしていることに依存します。一般的なアプリケーションコードでは、スピンロックを避けたいと思うでしょう。
いくつかの指示の間だけロックを保持し、待ち時間が重要である低レベルのものでは、スピンロックマットはロックよりも優れたソリューションです。ただし、特にC#が通常使用される種類のアプリケーションでは、このようなケースはまれです。
C#では、「スピンロック」は、私の経験では、ほとんどの場合、ロックを取得するよりも悪いものでした。スピンロックがロックよりもパフォーマンスが優れていることはまれです。
ただし、常にそうであるとは限りません。 .NET 4は、 System.Threading.SpinLock 構造を追加しています。これは、ロックが非常に短時間保持され、繰り返しつかまれる状況で利点を提供します。並列プログラミングの データ構造 に関するMSDNドキュメントから:
ロックの待機時間が短いと予想されるシナリオでは、SpinLockは他の形式のロックよりも優れたパフォーマンスを提供します。
スピンロックは、ツリーを介してロックするようなことをしている場合、他のロックメカニズムよりもパフォーマンスが優れている可能性があります。各ノードに非常に短い期間しかロックがない場合、従来のロックよりもパフォーマンスが優れている可能性があります。ある時点で、マルチスレッドのシーン更新を使用してレンダリングエンジンでこれに遭遇しました。つまり、Monitor.Enterを使用したロックよりも優れたパフォーマンスを発揮するようにプロファイルされたスピンロックです。
特にデバイスドライバーを使用したリアルタイムの作業では、かなりの量を使用しました。 (最後にこれを計ったとき)ハードウェア割り込みに関連付けられたセマフォのような同期オブジェクトを待つと、実際に割り込みが発生するのにどれだけ時間がかかっても、少なくとも20マイクロ秒がかみ砕かれます。メモリマップドハードウェアレジスタの1回のチェックと、それに続くRDTSCへのチェック(マシンをロックしないようにタイムアウトを可能にするため)は、高ナノ秒範囲(基本的にはノイズの低下)にあります。それほど時間はかからないハードウェアレベルのハンドシェイクの場合、スピンロックを打ち負かすのは非常に困難です。
私の2c:更新がいくつかのアクセス基準を満たしている場合、それらは適切なスピンロック候補です。
生成する可能性のあるものについては、通知されたロック構造(イベント、ミューテックス、セマフォなど)を使用する必要があります。
スピンロックのユースケースの1つは、競合が非常に少ないと予想されるが、競合が多くなる場合です。再帰的ロックのサポートが必要ない場合は、スピンロックを1バイトで実装できます。競合が非常に少ない場合は、CPUサイクルの浪費はごくわずかです。
実際のユースケースでは、何千もの要素の配列があり、配列のさまざまな要素への更新を安全に並行して行うことができます。 2つのスレッドが同じ要素を同時に更新しようとする可能性は非常に低いですが(競合が少ない)、要素ごとに1つのロックが必要です(多くの要素が必要になります)。このような場合、私は通常、並列に更新する配列と同じサイズのubytesの配列を割り当て、(Dプログラミング言語で)次のようにスピンロックをインラインで実装します。
while(!atomicCasUbyte(spinLocks[i], 0, 1)) {}
myArray[i] = newVal;
atomicSetUbyte(spinLocks[i], 0);
一方、通常のロックを使用する必要がある場合は、ポインターの配列をオブジェクトに割り当ててから、この配列の各要素にミューテックスオブジェクトを割り当てる必要があります。上記のようなシナリオでは、これはまったく無駄です。
パフォーマンスが重要なコードがある場合および現在よりも高速である必要があると判断した場合および重要な要素はロック速度であると判断した場合は、 dスピンロックを試すことをお勧めします。他の場合、なぜわざわざ?通常のロックは正しく使用する方が簡単です。
次の点に注意してください。
ほとんどのミューテックスの実装は、スレッドが実際にスケジュール解除される前に、しばらくの間スピンします。このため、これらのミューテックスを純粋なスピンロックと比較することは困難です。
同じスピンロック上で「できるだけ速く」スピンする複数のスレッドは、すべての帯域幅を混乱させ、プログラムの効率を大幅に低下させます。スピニングループにnoopを追加して、小さな「スリープ」時間を追加する必要があります。
スピンロックを回避する必要がある場合でも、アプリケーションコードでスピンロックを使用する必要はほとんどありません。
通常のOSで実行されているc#コードでスピンロックを使用する理由は何もありません。ビジーロックは、ほとんどの場合、アプリケーションレベルでは無駄です。回転すると、CPUタイムスライス全体が使用される可能性がありますが、ロックを使用すると、必要に応じてすぐにコンテキストスイッチが発生します。
スレッド数がnr =プロセッサ/コア数がnrの高性能コードが役立つ場合もありますが、そのレベルでパフォーマンスの最適化が必要な場合は、同期プリミティブが不十分な組み込みOSで作業し、 OS /ドライバー、またはいずれの場合もc#を使用していません。
[〜#〜] hlvm [〜#〜] プロジェクトでは、ガベージコレクターのストップザワールドフェーズにスピンロックを使用しました。これは簡単で、おもちゃのVMだからです。ただし、スピンロックは、そのコンテキストでは逆効果になる可能性があります。
Glasgow Haskellコンパイラのガベージコレクタのパフォーマンスバグの1つは非常に煩わしいため、「 最後のコアスローダウン "」という名前が付けられています。これは、GCでのスピンロックの不適切な使用の直接的な結果であり、そのスケジューラーのためにLinuxで悪化しますが、実際、他のプログラムがCPU時間を競合しているときはいつでもその影響を観察できます。
効果は2番目のグラフで明らかです ここ そして最後のコアだけではなく ここ に影響を及ぼしているのを見ることができます。
spinlocksを使用するときは、常にこれらの点に注意してください。
誰かがスピンロックを使用するのは良い考えだと思ったという理由だけで、私は個人的に非常に多くのデッドロックを見てきました。
スピンロックを使用するときは非常に注意してください
(私はこれを十分に強調することはできません)。