Man*_*Row 14 x86 assembly gcc synchronization spinlock
我是新手使用gcc内联汇编,并且想知道在x86多核机器上是否可以实现自旋锁(没有竞争条件)(使用AT&T语法):
spin_lock: mov 0 eax lock cmpxchg 1 [lock_addr] jnz spin_lock ret spin_unlock: lock mov 0 [lock_addr] ret
Cor*_*son 24
你有正确的想法,但你的asm被打破了:
cmpxchg 不能使用立即操作数,只能使用寄存器.
lock不是有效的前缀mov. mov对齐的地址在x86上是原子的,所以你lock无论如何都不需要.
自从我使用AT&T语法已有一段时间了,希望我记得一切:
spin_lock:
xorl %ecx, %ecx
incl %ecx # newVal = 1
spin_lock_retry:
xorl %eax, %eax # expected = 0
lock; cmpxchgl %ecx, (lock_addr)
jnz spin_lock_retry
ret
spin_unlock:
movl $0, (lock_addr) # atomic release-store
ret
Run Code Online (Sandbox Code Playgroud)
请注意,GCC具有原子内置,因此您实际上不需要使用内联asm来完成此任务:
void spin_lock(int *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1));
}
void spin_unlock(int volatile *p)
{
asm volatile ("":::"memory"); // acts as a memory barrier.
*p = 0;
}
Run Code Online (Sandbox Code Playgroud)
正如Bo在下文所述,锁定指令会产生成本:您使用的每个指令都必须获得对缓存行的独占访问权限并在lock cmpxchg运行时将其锁定,例如对于该缓存行的正常存储,但在lock cmpxchg执行期间保留.这可以延迟解锁线程,尤其是在多个线程正在等待锁定时.即使没有很多CPU,它仍然很容易并且值得优化:
void spin_lock(int volatile *p)
{
while(!__sync_bool_compare_and_swap(p, 0, 1))
{
// spin read-only until a cmpxchg might succeed
while(*p) _mm_pause(); // or maybe do{}while(*p) to pause first
}
}
Run Code Online (Sandbox Code Playgroud)
pause当您拥有像这样旋转的代码时,该指令对HyperThreading CPU的性能至关重要 - 它允许第二个线程在第一个线程旋转时执行.在不支持的CPU上pause,它被视为a nop.
pause当离开自旋循环时,它还可以防止内存顺序错误推测,当它终于再次进行实际工作时. x86中"PAUSE"指令的目的是什么?
请注意,旋转锁实际上很少使用:通常,使用类似临界区或futex的东西.这些集成了自旋锁以在低争用下实现性能,但随后又回退到OS辅助的睡眠和通知机制.他们也可能采取措施来提高公平性,以及cmpxchg/ pause循环不做的许多其他事情.
另请注意,cmpxchg对于简单的自旋锁是不必要的:您可以使用xchg然后检查旧值是否为0.在locked指令中减少工作量可以使高速缓存行固定更短的时间.有关使用和完成asm实现的内联汇编,请参阅内存操作锁定(但仍然没有回退到OS辅助的睡眠,只是无限期地旋转.)xchgpause
| 归档时间: |
|
| 查看次数: |
11810 次 |
| 最近记录: |