26

パフォーマンスが重要なC++でマルチスレッドアプリケーションを作成しています。スレッド間で小さな構造をコピーするときは、多くのロックを使用する必要があります。このため、スピンロックを使用することを選択しました。

私はこれについていくつかの調査と速度テストを行いましたが、ほとんどの実装はほぼ同じくらい高速であることがわかりました。

  • SpinCountが1000に設定されたMicrosoftのCRITICAL_SECTIONは、約140時間単位をスコアリングします
  • このアルゴリズムをMicrosoftのInterlockedCompareExchangeスコアで実装すると、約95時間単位になります。
  • また、このコード__asm {}のようなものを使用してインラインアセンブリを使用しようとしましたが、スコアは約70時間単位ですが、適切なメモリバリアが作成されているかどうかはわかりません。

編集:ここに示されている時間は、2つのスレッドがスピンロックを1,000,000回ロックおよびロック解除するのにかかる時間です。

これは大きな違いではないことは知っていますが、スピンロックは頻繁に使用されるオブジェクトであるため、プログラマーはスピンロックを作成するための可能な限り最速の方法に同意したと思います。しかし、それをグーグルすると、多くの異なるアプローチにつながります。この前述の方法CMPXCHG8Bは、 32ビットレジスタを比較する代わりにインラインアセンブリと命令を使用して実装した場合に最速になると思います。さらに、メモリバリアを考慮に入れる必要があります。これは、コア間の共有メモリに対する「排他的権利」を保証するLOCK CMPXHG8B(私は思いますか?)によって行うことができます。ついに[いくつかの提案]忙しい待ち時間にはNOP:REPを伴うべきだとこれにより、ハイパースレッディングプロセッサが別のスレッドに切り替えることができるようになりますが、これが正しいかどうかはわかりません。

さまざまなスピンロックのパフォーマンステストから、大きな違いはないことがわかりますが、純粋に学術的な目的のために、どれが最も速いかを知りたいと思います。ただし、アセンブリ言語とメモリバリアの経験は非常に限られているため、次のテンプレートでLOCKCMPXCHG8Bと適切なメモリバリアを使用して提供した最後の例のアセンブリコードを誰かが記述できれば幸いです。

__asm
{
     spin_lock:
         ;locking code.
     spin_unlock:
         ;unlocking code.
}
4

5 に答える 5

10

すでに受け入れられている回答がありますが、すべての回答を改善するために使用できる見逃したことがいくつかあります

  1. アトミック命令ではなく、揮発性読み取りでスピンします。これにより、特に競合の激しいロックで、不要なバス ロックが回避されます。
  2. 競合の激しいロックにはバックオフを使用する
  3. ロックをインライン化します。できれば、インライン asm が有害なコンパイラ (基本的には MSVC) の組み込み関数を使用します。
于 2012-08-16T04:22:56.773 に答える
5

ウィキペディアにはスピンロックに関する優れた記事があります。ここに x86 の実装があります

http://en.wikipedia.org/wiki/Spinlock#Example_implementation

x86 では "xchg" 命令に対して冗長であるため、それらの実装では "lock" プレフィックスを使用していないことに注意してください。この Stackoverflow ディスカッションで説明されているように、暗黙的にロック セマンティクスがあります。

マルチコア x86 では、XCHG のプレフィックスとして LOCK が必要ですか?

REP:NOP は PAUSE 命令のエイリアスです。詳細については、こちらをご覧ください。

x86 一時停止命令はスピンロックでどのように機能しますか? また、他のシナリオで使用できますか?

メモリバリアの問題について、知りたいことはすべてここにあります

メモリ バリア: ソフトウェア ハッカーのためのハードウェア ビュー (Paul E. McKenney 著)

http://irl.cs.ucla.edu/~yingdi/paperreading/whymb.2010.06.07c.pdf

于 2012-08-14T19:57:46.193 に答える
3

ここを見てください: cmpxchgを使用したx86スピンロック

コーリー・ネルソンに感謝

__asm{
spin_lock:
xorl %ecx, %ecx
incl %ecx
spin_lock_retry:
xorl %eax, %eax
lock; cmpxchgl %ecx, (lock_addr)
jnz spin_lock_retry
ret

spin_unlock:
movl $0 (lock_addr)
ret
}

そして、別のソースは次のように述べています: http://www.geoffchappell.com/studies/windows/km/cpu/cx8.htm

       lock    cmpxchg8b qword ptr [esi]
is replaceable with the following sequence

try:
        lock    bts dword ptr [edi],0
        jnb     acquired
wait:
        test    dword ptr [edi],1
        je      try
        pause                   ; if available
        jmp     wait

acquired:
        cmp     eax,[esi]
        jne     fail
        cmp     edx,[esi+4]
        je      exchange

fail:
        mov     eax,[esi]
        mov     edx,[esi+4]
        jmp     done

exchange:
        mov     [esi],ebx
        mov     [esi+4],ecx

done:
        mov     byte ptr [edi],0

そして、ロックフリーとロックの実装についての議論があります: http://newsgroups.derkeiler.com/Archive/Comp/comp.programming.threads/2011-10/msg00009.html

于 2012-08-14T19:36:12.093 に答える
-1

ただ尋ねる:

スピンロックとほぼロックのないデータ構造を深く掘り下げる前に:

ベンチマークとアプリケーションで、競合するスレッドが異なるコアで実行されることが保証されていることを確認しましたか?

そうでない場合、開発マシンでは問題なく動作するが、1 つのスレッドがスピンロックのロッカーとロック解除の両方を行う必要があるため、現場ではうまくいかない/失敗するプログラムになってしまう可能性があります。

あなたに数字を与えるために:Windowsでは、10ミリ秒の標準タイムスライスがあります。2 つの物理スレッドがロック/ロック解除に関与していることを確認しないと、1 秒あたり約 500 回のロック/ロック解除が発生し、この結果は非常に悪くなります。

于 2012-08-14T20:19:55.857 に答える