x86_64和ARM上的原子CAS操作是否总是使用std :: memory_order_seq_cst?

时间:2013-09-02 16:38:25

标签: c++ c++11 x86 arm compare-and-swap

作为Anthony Williams said

  

some_atomic.load(std :: memory_order_acquire)只是一直到   一个简单的加载指令,和   some_atomic.store(std :: memory_order_release)简单到了   商店指导。

众所周知,在 x86 上,操作load()store()内存障碍memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel不需要处理器指令。

但在 ARMv8 上,我们知道load()store()存在以下内存障碍: http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-1-of-2 http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2

关于CPU的不同架构:http://g.oswego.edu/dl/jmm/cookbook.html

接下来,但对于 x86 上的CAS操作,具有不同内存屏障的这两行在反汇编代码中是相同的( MSVS2012 x86_64 ):

    a.compare_exchange_weak(temp, 4, std::memory_order_seq_cst, std::memory_order_seq_cst);
000000013FE71A2D  mov         ebx,dword ptr [temp]  
000000013FE71A31  mov         eax,ebx  
000000013FE71A33  mov         ecx,4  
000000013FE71A38  lock cmpxchg dword ptr [temp],ecx  

    a.compare_exchange_weak(temp, 5, std::memory_order_relaxed, std::memory_order_relaxed);
000000013FE71A4D  mov         ecx,5  
000000013FE71A52  mov         eax,ebx  
000000013FE71A54  lock cmpxchg dword ptr [temp],ecx  

GCC 4.8.1 x86_64 - GDB 编译的反汇编代码:

a.compare_exchange_weak(temp, 4, std::memory_order_seq_cst, std::memory_order_seq_cst);
a.compare_exchange_weak(temp, 5, std::memory_order_relaxed, std::memory_order_relaxed);

0x4613b7  <+0x0027>         mov    0x2c(%rsp),%eax
0x4613bb  <+0x002b>         mov    $0x4,%edx
0x4613c0  <+0x0030>         lock cmpxchg %edx,0x20(%rsp)
0x4613c6  <+0x0036>         mov    %eax,0x2c(%rsp)
0x4613ca  <+0x003a>         lock cmpxchg %edx,0x20(%rsp)

在x86 / x86_64平台上是否有任何原子CAS操作,像atomic_val.compare_exchange_weak(temp, 1, std::memory_order_relaxed, std::memory_order_relaxed);这样的例子总是满足于排序std::memory_order_seq_cst

如果x86上的任何CAS操作始终以顺序一致性(std::memory_order_seq_cst)运行而不考虑障碍,那么在ARMv8上它是一样的吗?

问题: std::memory_order_relaxed CAS的顺序是否应阻止x86或ARM上的内存总线?

答案 x86任何compare_exchange_weak() 操作与任何std::memory_orders(偶数std::memory_order_relaxed始终转换为LOCK CMPXCHG with lock bus, to be really atomicXCHG - "the cmpxchg is just as expensive as the xchg instruction"的费用相同。

(添加:XCHG等于LOCK XCHG,但CMPXCHG不等于LOCK CMPXCHG(这实际上是原子的)

对于任何`compare_exchange_weak()的 ARM和PowerPC,对于不同的std :: memory_orders,是不同的锁处理器指令,通过LL/SC

x86(CAS除外),ARM和PowerPC的处理器内存屏障指令:http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

3 个答案:

答案 0 :(得分:5)

您不应该担心编译器将给定C11构造映射到哪些指令,因为这不会捕获所有内容。相反,您需要开发有关C11内存模型保证的代码。如上面的注释所述,只要不违反C11内存模型,您的编译器或未来的编译器就可以自由地重新排序宽松的内存操作。通过像CDSChecker这样的工具运行代码也是值得的,看看内存模型下允许的行为。

答案 1 :(得分:1)

x86保证订购负载后的负载,并订购后续存储的存储。鉴于CAS需要加载和存储,所有操作都必须在它周围进行排序。

但是,值得注意的是,在存在多个带​​有memory_order_relaxed的原子的情况下,允许编译器对它们进行重新排序。使用memory_order_seq_cst不能这样做。

答案 2 :(得分:1)

我认为编译器即使对lock cmpxchg也会发出memory_order_relaxed,因为这是确保compare + exchange本身实际上是原子的唯一方法。就像artless_noise在评论中所说,其他架构可以使用Load Linked / Store Conditional来实现compare_exchange_weak(...)

memory_order_relaxed仍然应该让编译器将其他变量的存储从循环中提升出来,否则在编译时重新排序内存访问。

如果有一种方法可以在x86上执行它,这也不是一个完整的内存屏障,一个好的编译器会将它用于memory_order_relaxed