基本上,我要寻找的是能够原子地完成以下逻辑的东西。
#define FAILED 1
#define SUCCESS 0
int foo (uint64_t * src, uint64_t * dst, uint64_t expected) {
if (*src == expected) {
*dst = *src;
return SUCCESS;
}
return FAILURE;
}
对于这个特定的用例,在所有情况下expected == 0
和*dst
都不能被任何其他线程修改。然而,*src
可以由其他线程同时修改(这些其他线程可以在另一个内核上,否则我可以使用可重启序列(
如果*src != expected
(带有expected == 0
(,则不能修改*dst
。
我有不变的*src != *dst
,除非是*src == *dst == 0
(这可能有助于找到解决方案(。
最后,如果它能够实现任何潜在的解决方案,我可以保证*src
和*dst
要么在同一个缓存线上,要么在不同的缓存线上。
我不相信任何内置的原子操作(https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html)可以实现这一点,所以我认为唯一的方法是使用lock
前缀的一些内联程序集,或者使用某些函数的一些副作用(即CAS将在失败时设置预期的事实(。
总结一下,我正在寻找一种用内联asm实现自定义原子操作的方法,或者我可以使用bultin原子来实现这一点的某种方法。
谢谢!
我不明白你想要这个原语做什么,我认为把它称为"原子";。如果在任何时刻观察到*src
具有expected
的值,则发生对*dst
的存储是有效的。任何其他线程都无法观察到该操作的原子性或缺乏原子性。因此,按照您编写的方式编写它(尽管*src
具有适当的原子类型(应该没问题。
如果你不同意这种推理,请解释一下你想在其中使用原语的更大范围,因为它几乎肯定是错误的,而真正的问题是理解它是如何错误的,并找出适合它的东西。(解决方案几乎肯定是停止尝试使用原子论并使用锁。(
一个普通的CAS只有一个内存操作数!这就像是一个部分DCAS,也就是CAS2。
如果将src和dst彼此相邻,则可以使用宽CAS(如lock cmpxchg16b
(来模拟它,但如果dst
发生变化,则可能会错误地失败。
或者,如果您的CPU未通过微码更新禁用,则可以使用Intel TSX事务内存。(我认为,由于某种MDS漏洞的原因,HLE在微码中被禁用,但AFAIK RTM仍然可以在CPU上使用,因为正确性的原因,即在Skylake上工作,稍后至少在一些Broadwell上工作。(
请注意,整个缓存线的AVX-512负载并不能保证是原子的,但在所有当前的AVX-512CPU上,人们认为它们在实践中是原子的。不幸的是,即使16字节SIMD加载/存储原子性很普遍,供应商也不愿意提供任何CPUID方法来检测大于8字节的原子性保证。注意,内核之间的互连可能会导致撕裂,而不仅仅是狭窄的加载/存储执行单元:SSE指令:哪些CPU可以执行原子16B内存操作?显示了由于HyperTransport,K10 Opteron上不同套接字中的内核之间仅的8字节边界撕裂。