我正在阅读[lock cmpxchg
说明])https://www.felixcloutier.com/x86/CMPXCHG.html):
此指令可与
LOCK
前缀一起使用以允许 以原子方式执行的指令。简化 接口到处理器的总线,即目标操作数 接收写周期而不考虑结果 比较。如果,则写回目标操作数 比较失败;否则,源操作数是 写入目的地。 (处理器永远不会 产生锁定读取而不产生锁定 写。)
现在考虑执行lock cmpxchg
的两个线程:
Thread 1 Thread 2
mov ebx, 0x4000 mov ebx, 0x4000 ; address
mov edx, 0x62ab6 mov edx, 0x62ab8 ; new val
mov eax, 0x62ab1 mov eax, 0x62ab1 ; old
lock cmpxchg [ebx], eax lock cmpxchg [ebx], eax ; <----- here
问题是在线程1和线程2中都可以锁定cmpxchg
失败吗?
由于
目标操作数 接收写周期而不考虑结果 比较
我可以猜测两个线程都可以具有写周期,并且由于与过时值相比,它们都可以被还原......但是我不确定这是否正确。
也许我需要查看cas实现细节,但是没有在intel指令参考中指定(至少我找不到)
答案 0 :(得分:3)
我的理解是lock cmpxchg
不能虚假地失败 - 与LL / SC不同 - 假设内存地址的值确实匹配。它通过获取高速缓存行的独占所有权来构建来自高速缓存一致性协议的那些保证,并且在操作完成之前不将其产生到其他核心。
因此,如果某个其他线程写入内存位置,CAS只能对所有线程失败。
答案 1 :(得分:1)
@ the8472的回答是正确的,但我想补充一个替代答案。
https://www.felixcloutier.com/x86/CMPXCHG.html已经足够详细地指定行为以排除虚假失败的可能性。如果由于内存中的值与eax
不匹配而导致其失败,则文档必须这样说。
您还可以注意到编译器对C++11 std::atomic::compare_exchange_strong
使用单lock cmpxchg
这一事实,您可以从中得出结论,编译器编写者认为lock cmpxchg
不能虚假地失败。
#include <atomic>
bool cas_bool(std::atomic_int *a, int expect, int want) {
return a->compare_exchange_strong(expect, want);
}
cas_bool(std::atomic<int>*, int, int):
mov eax, esi
lock cmpxchg DWORD PTR [rdi], edx
sete al
ret
另请参阅Can num++ be atomic for 'int num'?,详细了解内部实施lock
指令的方式,以及它们与MESI的互动方式。 (即 @ the8472的答案是短版本:对于不跨越高速缓存行的操作数,核心只挂起到该高速缓存行,因此系统中的其他任何内容都无法读取或写入{ {1}} 强>)。
目标操作数接收写周期而不考虑比较结果
对于系统中的所有其他观察者,读/写对是原子的。你提出的read1 / read2 / write1 / abort write2的顺序是不可能的,因为lock cmpxchg
是原子的,所以lock cmpxchg
不能出现在全局顺序中的read1和write1之间。
此外,该语言仅适用于外部存储器总线。具有集成内存控制器的现代CPU可以执行他们想要的任何操作(对于分布在两个缓存行中的地址上的read2
)。英特尔可能会发布主板供应商的文档,用于对内存总线上的信号进行内部测试。
该文档可能仍然与MMIO地址上的lock cmpxchg
相关,但绝对不适用于回写内存中的对齐操作数。在这种情况下,它只是一个缓存锁定。(当比较失败时,是否写入L1d缓存是一个隐藏的实现细节)。我想你可以通过查看它是否弄脏缓存行来测试它(即将其置于修改状态而不是独占)。
有关lock cmpxchg
如何在内部与lock cmpxchg
之间发挥作用的更多讨论,请参阅我在Exit critical region上的答案后与我和@BeeOnRope之间的聊天帖子。 (大多数情况下,我的想法可能在理论上有效,但与我们对英特尔x86 CPU的了解不相符,而且@BeeOnRope指出了我的错误。https://chat.stackoverflow.com/transcript/message/42472667#42472667。我们很少能够确定有关细节的细节。 xchg
与xchg
的效率。lock cmpxchg
当然可以使缓存行保持锁定的周期少于xchg
,但需要对其进行测试。我认为{{但是,如果在单个线程的相同位置背靠背使用,则会有更好的延迟。)