假定体系结构是ARM64或x86-64。
我想确定这两个是否相等:
a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
MyBarrier(); a = *(volatile __int64*)p; MyBarrier();
MyBarrier()
是编译器级别的存储屏障(提示),例如__asm__ __volatile__ ("" ::: "memory")
。
因此,方法2应该比方法1更快。
我听说_Interlocked()
函数还将暗示编译器和硬件级别的内存障碍。
我听说在这些体系结构上读取(适当对齐的)固有数据是原子的,但是我不确定方法2是否可以广泛使用?
(ps。因为我认为CPU将自动处理数据依赖关系,所以这里不考虑硬件障碍。)
感谢您对此进行任何建议 / 更正。
以下是常春藤桥(i5笔记本电脑)上的一些基准测试。
(1E + 006循环: 27毫秒):
; __int64 a = _InterlockedCompareExchange64((__int64*)p, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR val$[rsp], rbx
(1E + 006循环: 27毫秒):
; __faststorefence(); __int64 a = *(volatile __int64*)p;
lock or DWORD PTR [rsp], 0
mov rcx, QWORD PTR val$[rsp]
(1E + 006循环: 7毫秒):
; _mm_sfence(); __int64 a = *(volatile __int64*)p;
sfence
mov rcx, QWORD PTR val$[rsp]
(1E + 006循环: 1.26ms ,未同步?):
; __int64 a = *(volatile __int64*)p;
mov rcx, QWORD PTR val$[rsp]
答案 0 :(得分:1)
要使第二个版本在功能上等效,您显然需要原子64位读取,这在您的平台上是正确的。
但是,_MemoryBarrier()
不是“编译器提示”。 x86上的_MemoryBarrier()
可以防止编译器和CPU重新排序,并且还可以确保写入后具有全局可见性。您可能还只需要第一个_MemoryBarrier()
,第二个可以用_ReadWriteBarrier()
替换,除非a
也是一个共享变量-但由于您正在阅读,因此甚至不需要通过易失性指针,这将防止任何编译器在MSVC中重新排序。
创建此替换项时,基本上会得到几乎same result:
// a = _InterlockedCompareExchange64((__int64*)&val, 0, 0);
xor eax, eax
lock cmpxchg QWORD PTR __int64 val, r8 ; val
// _MemoryBarrier(); a = *(volatile __int64*)&val;
lock or DWORD PTR [rsp], r8d
mov rax, QWORD PTR __int64 val ; val
在我的i7 Ivy Bridge笔记本电脑上以循环方式运行这两个程序,结果相等,误差在2-3%之内。
但是,有两个内存障碍,“优化版本”实际上要慢2倍左右。
因此,更好的问题是:您为什么要使用_InterlockedCompareExchange64
??如果您需要对变量的原子访问,请使用std::atomic
,并且优化编译器应进行编译使其成为您的体系结构最优化的版本,并添加所有必要的障碍以防止重新排序并确保缓存一致性。