`std::memory_order_acquire` 的语义是否要求在 x86/x86_64 处理器上执行指令?

8
众所周知,在x86架构上,对于操作load()和store(),内存屏障memory_order_consume、memory_order_acquire、memory_order_release和memory_order_acq_rel不需要处理器缓存和流水线的指令,汇编代码总是对应于std::memory_order_relaxed。这些限制只是为了编译器的优化而必要:http://www.stdthread.co.uk/forum/index.php?topic=72.0 而这段反汇编代码证实了对于store() (MSVS2012 x86_64),上述观点的正确性:
std::atomic<int> a;
    a.store(0, std::memory_order_relaxed);
000000013F931A0D  mov         dword ptr [a],0  
    a.store(1, std::memory_order_release);
000000013F931A15  mov         dword ptr [a],1  

但是这段代码没有为 load()MSVS2012 x86_64)进行确认,而是使用了 lock cmpxchg

    int val = a.load(std::memory_order_acquire);
000000013F931A1D  prefetchw   [a]  
000000013F931A22  mov         eax,dword ptr [a]  
000000013F931A26  mov         edx,eax  
000000013F931A28  lock cmpxchg dword ptr [a],edx  
000000013F931A2E  jne         main+36h (013F931A26h)  

    std::cout << val << "\n";

但是安东尼·威廉姆斯(Anthony Williams)说:

some_atomic.load(std::memory_order_acquire)只是简单地执行一个简单的加载指令,而some_atomic.store(std::memory_order_release)只是简单的存储指令。

我错了吗?在x86/x86_64处理器上,std::memory_order_acquire的语义是否需要使用lock cmpxchg或者只是像安东尼·威廉姆斯所说的那样,一个简单的加载指令mov

回答:这与此错误报告相同:http://connect.microsoft.com/VisualStudio/feedback/details/770885


1
我不确定查看编译器生成的内容是否是确定特定功能需求的好方法 - 编译器做的事情可能比实际需要的要多。 - Mats Petersson
@Mats Petersson 好的。但是volatile早在很久以前就出现了,当时还没有关于std::memory_order的任何了解。为了避免不必要的调用WinAPI或汇编代码,他们决定对volatile使用屏障(lock) - 这三种解决方案同样不美观。但是现在有了新的C++11标准,所有内容都已经明确定义,并且有一个优雅的解决方案 - mov。也许对于旧的x86处理器需要在load()中加锁? - Alex
我的观点是编译器不必为任何特定的结构生成最有效的代码 - 如果这是一个严格的要求,-O3不会比-O0生成更好的代码,简单地说。当然,这完全有可能是在std::atomic内部使用volatile所导致的人为/错误(我相信标准要求使用volatile)。 - Mats Petersson
1
这是否与此错误报告相同?http://connect.microsoft.com/VisualStudio/feedback/details/770885 - jcoder
@jcoder 如果您知道的话,这可能也是MSVS2012的一个bug吗? https://dev59.com/uHbZa4cB1Zd3GeqPFGKo - Alex
显示剩余5条评论
1个回答

8
不需要在x86/x86_64上使用处理器指令来执行std::memory_order_acquire的语义。
除了atomic.store(val, std::memory_order_seq_cst)需要(LOCK) XCHG或替代方法: MOV (into memory),MFENCE之外,x86_64上的任何load()/store()操作都不需要处理器指令(lock/fence)。
对于x86(except CAS),以及ARM和PowerPC处理器的内存屏障指令: http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html 反汇编器GCC 4.8.1 x86_64 - GDB - load():
    20      temp = a.load(std::memory_order_relaxed);
    21      temp = a.load(std::memory_order_acquire);
    22      temp = a.load(std::memory_order_seq_cst);
0x46140b  <+0x007b>         mov    0x38(%rsp),%ebx
0x46140f  <+0x007f>         mov    0x34(%rsp),%esi
0x461413  <+0x0083>         mov    0x30(%rsp),%edx

反汇编器 GCC 4.8.1 x86_64 - GDB - store()

a.store(temp, std::memory_order_relaxed);
a.store(temp, std::memory_order_release);
a.store(temp, std::memory_order_seq_cst);
0x4613dc  <+0x004c>         mov    %eax,0x20(%rsp)
0x4613e0  <+0x0050>         mov    0x38(%rsp),%eax
0x4613e4  <+0x0054>         mov    %eax,0x20(%rsp)
0x4613e8  <+0x0058>         mov    0x38(%rsp),%eax
0x4613ec  <+0x005c>         mov    %eax,0x20(%rsp)
0x4613f0  <+0x0060>         mfence
0x4613f3  <+0x0063>         mov    %ebx,0x20(%rsp)

反汇编器 MSVS 2012 x86_64 - load() - 它与此错误报告相同:http://connect.microsoft.com/VisualStudio/feedback/details/770885

    temp = a.load(std::memory_order_relaxed);
000000013FE51A1F  prefetchw   [a]  
000000013FE51A24  mov         eax,dword ptr [a]  
000000013FE51A28  nop         dword ptr [rax+rax]  
000000013FE51A30  mov         ecx,eax  
000000013FE51A32  lock cmpxchg dword ptr [a],ecx  
000000013FE51A38  jne         main+40h (013FE51A30h)  
000000013FE51A3A  mov         dword ptr [temp],eax  
    temp = a.load(std::memory_order_acquire);
000000013FE51A3E  prefetchw   [a]  
000000013FE51A43  mov         eax,dword ptr [a]  
000000013FE51A47  nop         word ptr [rax+rax]  
000000013FE51A50  mov         ecx,eax  
000000013FE51A52  lock cmpxchg dword ptr [a],ecx  
000000013FE51A58  jne         main+60h (013FE51A50h)  
000000013FE51A5A  mov         dword ptr [temp],eax  
    temp = a.load(std::memory_order_seq_cst);
000000013FE51A5E  prefetchw   [a]  
    temp = a.load(std::memory_order_seq_cst);
000000013FE51A63  mov         eax,dword ptr [a]  
000000013FE51A67  nop         word ptr [rax+rax]  
000000013FE51A70  mov         ecx,eax  
000000013FE51A72  lock cmpxchg dword ptr [a],ecx  
000000013FE51A78  jne         main+80h (013FE51A70h)  
000000013FE51A7A  mov         dword ptr [temp],eax  

反汇编器 MSVS 2012 x86_64 - store()

    a.store(temp, std::memory_order_relaxed);
000000013F8C1A58  mov         eax,dword ptr [temp]  
000000013F8C1A5C  mov         dword ptr [a],eax  

    a.store(temp, std::memory_order_release);
000000013F8C1A60  mov         eax,dword ptr [temp]  
000000013F8C1A64  mov         dword ptr [a],eax  

    a.store(temp, std::memory_order_seq_cst);
000000013F8C1A68  mov         eax,dword ptr [temp]  
000000013F8C1A6C  xchg        eax,dword ptr [a]  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接