MOV x86 指令是否实现 C++11 memory_order_release 原子存储?

Does the MOV x86 instruction implement a C++11 memory_order_release atomic store?

据此https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html, 已释放的存储在 x86(包括 x86-64)上实现为 MOV(进入内存)。

根据他http://en.cppreference.com/w/cpp/atomic/memory_order

memory_order_release:

A store operation with this memory order performs the release operation: no memory accesses in the current thread can be reordered after this store. This ensures that all writes in the current thread are visible in other threads that acquire or the same atomic variable and writes that carry a dependency into the atomic variable become visible in other threads that consume the same atomic.

我了解到当使用memory_order_release时,之前完成的所有内存存储应该在这之前完成。

int a;
a = 10;
std::atomic<int> b;
b.store(50, std::memory_order_release); // i can be sure that 'a' is already 10, so processor can't reorder the stores to 'a' and 'b'

问题: 一个简单的 MOV 指令(没有明确的内存栅栏)怎么可能足以满足这种行为? MOV 如何告诉处理器完成所有先前的存储?

这确实是映射,至少在使用 Intel 编译器编译的代码中是这样,我在其中看到:

0000000000401100 <_Z5storeRSt6atomicIiE>:
  401100:       48 89 fa                mov    %rdi,%rdx
  401103:       b8 32 00 00 00          mov    [=10=]x32,%eax
  401108:       89 02                   mov    %eax,(%rdx)
  40110a:       c3                      retq
  40110b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

0000000000401110 <_Z4loadRSt6atomicIiE>:
  401110:       48 89 f8                mov    %rdi,%rax
  401113:       8b 00                   mov    (%rax),%eax
  401115:       c3                      retq
  401116:       0f 1f 00                nopl   (%rax)
  401119:       0f 1f 80 00 00 00 00    nopl   0x0(%rax)

代码:

#include <atomic>
#include <stdio.h>

void store( std::atomic<int> & b ) ;

int load( std::atomic<int> & b ) ;

int main()
{
   std::atomic<int> b ;

   store( b ) ;

   printf("%d\n", load( b ) ) ;

   return 0 ;
}

void store( std::atomic<int> & b )
{
   b.store(50, std::memory_order_release ) ;
}

int load( std::atomic<int> & b )
{
   int v = b.load( std::memory_order_acquire ) ;

   return v ;
}

当前的 Intel architecture documents,第 3 卷(系统编程指南)很好地解释了这一点。参见:

8.2.2 P6 和更新的处理器系列中的内存排序

  • 读数不会与其他读数一起重新排序。
  • 写入不会与较旧的读取一起重新排序。
  • 内存写入不会与其他写入一起重新排序,但以下情况除外:...

那里解释了完整的内存模型。我假设英特尔和 C++ 标准人员已经详细合作,为每个可能的内存顺序操作确定最佳映射,这符合第 3 卷中描述的内存模型,并且已经确定了普通存储和加载在这些情况下就足够了。

请注意,仅仅因为 x86-64 上的此有序存储不需要特殊说明,并不意味着这将是普遍正确的。对于 powerpc,我希望在存储中看到类似 lwsync 指令的东西,而在 hpux (ia64) 上,编译器应该使用 st4.rel 指令。

内存在 运行 时重新排序(由 CPU 完成),内存在编译时重新排序。请阅读 Jeff Preshing's article on compile-time reordering(以及该博客上的许多其他好文章)以获取更多信息。

memory_order_release 阻止编译器重新排序对数据的访问,以及发出任何必要的屏蔽或特殊指令。在 x86 asm 中,普通的加载和存储已经具有获取/释放语义,因此阻塞编译时重新排序足以 acq_rel,但不是 seq_cst.