gccインラインアセンブリを使用するのが初めてで、x86マルチコアマシンでスピンロック(競合条件なし)を(AT & T構文を使用して)実装できるかどうか疑問に思っていました。cmpxchgを使用したx86スピンロック
spin_lock: mov 0 eax lock cmpxchg 1 [lock_addr] jnz spin_lock ret spin_unlock: lock mov 0 [lock_addr] ret
gccインラインアセンブリを使用するのが初めてで、x86マルチコアマシンでスピンロック(競合条件なし)を(AT & T構文を使用して)実装できるかどうか疑問に思っていました。cmpxchgを使用したx86スピンロック
spin_lock: mov 0 eax lock cmpxchg 1 [lock_addr] jnz spin_lock ret spin_unlock: lock mov 0 [lock_addr] ret
あなたは正しい考えを持っているが、あなたのASMが壊れている:
cmpxchg
は、即値オペランドとレジスタのみを動作することはできません。
lock
は、mov
の有効な接頭辞ではありません。アライメントされたアドレスへのmov
は、x86ではアトミックなので、とにかくlock
は必要ありません。
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
注GCCは原子ビルトインを持っているので、あなたが実際にインラインアセンブラを使用する必要はありません:
私が& T構文で使用してきましたので、いくつかの時間となっているが、私はすべてを覚えて願っていますこの達成:Boは以下の言うように
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1));
}
void spin_unlock(int volatile *p)
{
asm volatile (""); // acts as a memory barrier.
*p = 0;
}
を、ロックされた命令がコストを負担:あなたが使用して一人一人が自分のキャッシュをフラッシュし、あなたが十分なCPUを持っている場合は非常に高価になることができ、システムのメモリバスをロックする必要があります。でも、多くのCPUせず、それはまだ簡単で価値がある、それは周りに最適化する:あなたはこのようにスピンコードを持っているとき
void spin_lock(int volatile *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1))
{
while(*p) _mm_pause();
}
}
pause
命令がハイパースレッディングのCPUのパフォーマンスのために不可欠である - それは、第二のスレッドが実行することができます最初のスレッドが回転しています。 pause
をサポートしないCPUでは、nop
として扱われます。
これは、メモリバス上の競合が少ないと入れます
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1)) while(*p);
}
合意しましたが、このコードはあまり良くありません。単純なwhile(* p)は、コンパイラーが簡単に最適化することができます。いくつかの壁を追加してください。また、Intelチップに_mm_pause()を追加すると、パフォーマンスが大幅に向上します。 –
無効spin_lockのパラメータは、()もvolatile宣言されるべきか? – ManRow
いいえ、 '__sync_bool_compare_and_swap'はすでに' volatile'として扱います。 –
'spin_unlock'の中のメモリバリアとして使用されるasmにはおそらくメモリクローバーが含まれているはずです。一方、「書き込み障壁を実行し、0を書き込む」ように設計された「__sync_lock_release」がありますが、それはまったく考える必要はなく、ややポータブルです。それは明示的に読取りの障壁として働いていません(それは実際にはターゲットアーキテクチャ上で実行されています)が、それは問題ありません。最悪の場合は、まれなケースの中で1つの特別なスピンを行う別のスレッドです。 – Damon