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

Kra*_*rab 6 c++ x86 memory-model c++11 stdatomic

根据这个https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html,已发布的商店MOV在x86(包括x86-64)上实现为(进入内存).

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

memory_order_release:

具有此内存顺序的存储操作将执行释放操作:在此存储之后,不能对当前线程中的内存访问进行重新排序.这确保了当前线程中的所有写入在获取或相同原子变量的其他线程中可见,并且带有依赖关系到原子变量的写入在消耗相同原子的其他线程中变得可见.

我知道当使用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'
Run Code Online (Sandbox Code Playgroud)

问题:MOV对于这种行为,裸指令(没有明确的内存栅栏)是否足够?如何MOV告诉处理器完成以前的所有商店?

Pee*_*oot 5

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

0000000000401100 <_Z5storeRSt6atomicIiE>:
  401100:       48 89 fa                mov    %rdi,%rdx
  401103:       b8 32 00 00 00          mov    $0x32,%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)
Run Code Online (Sandbox Code Playgroud)

对于代码:

#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 ;
}
Run Code Online (Sandbox Code Playgroud)

当前的英特尔架构文档,第 3 卷(系统编程指南)很好地解释了这一点。看:

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

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

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

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


ras*_*ash 5

在运行时对内存进行重新排序(由CPU完成),在编译时对内存进行重新排序。请阅读Jeff Preshing的有关编译时重新排序的文章(以及该博客上的许多其他不错的文章),以获取更多信息。

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