std :: memory_order_XXX如何工作

时间:2019-06-14 06:55:08

标签: c++ c++11 x86 memory-barriers memory-model

我不了解std :: memory_order_XXX(例如memory_order_release / memory_order_acquire ...)如何工作。

从某些文档中可以看出,这些内存模式具有不同的功能,但是我真的很困惑它们具有相同的汇编代码,是什么决定了差异

该代码:

    static std::atomic<long> gt;
     void test1() {
          gt.store(1, std::memory_order_release);
          gt.store(2, std::memory_order_relaxed);
          gt.load(std::memory_order_acquire);
          gt.load(std::memory_order_relaxed);
     }

对应于:

        00000000000007a0 <_Z5test1v>:
         7a0:   55                      push   %rbp
         7a1:   48 89 e5                mov    %rsp,%rbp
         7a4:   48 83 ec 30             sub    $0x30,%rsp

**memory_order_release:
         7a8:   48 c7 45 f8 01 00 00    movq   $0x1,-0x8(%rbp)
         7af:   00 
         7b0:   c7 45 e8 03 00 00 00    movl   $0x3,-0x18(%rbp)
         7b7:   8b 45 e8                mov    -0x18(%rbp),%eax
         7ba:   be ff ff 00 00          mov    $0xffff,%esi
         7bf:   89 c7                   mov    %eax,%edi
         7c1:   e8 b1 00 00 00          callq  877 <_ZStanSt12memory_orderSt23__memory_order_modifier>
         7c6:   89 45 ec                mov    %eax,-0x14(%rbp)
         7c9:   48 8b 55 f8             mov    -0x8(%rbp),%rdx
         7cd:   48 8d 05 44 08 20 00    lea    0x200844(%rip),%rax        # 201018 <_ZL2gt>
         7d4:   48 89 10                mov    %rdx,(%rax)
         7d7:   0f ae f0                mfence** 

**memory_order_relaxed:
         7da:   48 c7 45 f0 02 00 00    movq   $0x2,-0x10(%rbp)
         7e1:   00 
         7e2:   c7 45 e0 00 00 00 00    movl   $0x0,-0x20(%rbp)
         7e9:   8b 45 e0                mov    -0x20(%rbp),%eax
         7ec:   be ff ff 00 00          mov    $0xffff,%esi
         7f1:   89 c7                   mov    %eax,%edi
         7f3:   e8 7f 00 00 00          callq  877 <_ZStanSt12memory_orderSt23__memory_order_modifier>
         7f8:   89 45 e4                mov    %eax,-0x1c(%rbp)
         7fb:   48 8b 55 f0             mov    -0x10(%rbp),%rdx
         7ff:   48 8d 05 12 08 20 00    lea    0x200812(%rip),%rax        # 201018 <_ZL2gt>
         806:   48 89 10                mov    %rdx,(%rax)
         809:   0f ae f0                mfence** 

**memory_order_acquire:
         80c:   c7 45 d8 02 00 00 00    movl   $0x2,-0x28(%rbp)
         813:   8b 45 d8                mov    -0x28(%rbp),%eax
         816:   be ff ff 00 00          mov    $0xffff,%esi
         81b:   89 c7                   mov    %eax,%edi
         81d:   e8 55 00 00 00          callq  877 <_ZStanSt12memory_orderSt23__memory_order_modifier>
         822:   89 45 dc                mov    %eax,-0x24(%rbp)
         825:   48 8d 05 ec 07 20 00    lea    0x2007ec(%rip),%rax        # 201018 <_ZL2gt>
         82c:   48 8b 00                mov    (%rax),%rax**

**memory_order_relaxed:
         82f:   c7 45 d0 00 00 00 00    movl   $0x0,-0x30(%rbp)
         836:   8b 45 d0                mov    -0x30(%rbp),%eax
         839:   be ff ff 00 00          mov    $0xffff,%esi
         83e:   89 c7                   mov    %eax,%edi
         840:   e8 32 00 00 00          callq  877 <_ZStanSt12memory_orderSt23__memory_order_modifier>
         845:   89 45 d4                mov    %eax,-0x2c(%rbp)
         848:   48 8d 05 c9 07 20 00    lea    0x2007c9(%rip),%rax        # 201018 <_ZL2gt>
         84f:   48 8b 00                mov    (%rax),%rax**

         852:   90                      nop
         853:   c9                      leaveq 
         854:   c3                      retq   

        00000000000008cc <_ZStanSt12memory_orderSt23__memory_order_modifier>:
         8cc:   55                      push   %rbp
         8cd:   48 89 e5                mov    %rsp,%rbp
         8d0:   89 7d fc                mov    %edi,-0x4(%rbp)
         8d3:   89 75 f8                mov    %esi,-0x8(%rbp)
         8d6:   8b 55 fc                mov    -0x4(%rbp),%edx
         8d9:   8b 45 f8                mov    -0x8(%rbp),%eax
         8dc:   21 d0                   and    %edx,%eax
         8de:   5d                      pop    %rbp
         8df:   c3                      retq   

期望不同的内存模式在汇编代码上具有不同的工具, 但是设置不同的模式值对汇编没有影响,谁可以解释呢?

2 个答案:

答案 0 :(得分:3)

每个内存模型设置都有其语义。编译器必须满足这种语义,即:

  1. 它不允许编译器执行某些优化,例如读取和写入的重新排序。

  2. 它指示编译器将完全相同的消息传播到硬件。如何完成取决于平台。 x86_64本身提供了非常强大的内存模型。因此,在几乎所有情况下,无论选择哪种内存模型,您都不会在为x86_64生成的汇编代码中看到差异。但是,在RISC体系结构(例如ARM)上,您看到差异,因为编译器将不得不插入内存屏障。内存屏障的类型取决于所选的内存模型设置。

编辑:看看JSR-133。它很老,是关于Java的,但是从我所知道的编译器角度来看,它提供了关于内存模型的最好的解释。特别是,请查看不同体系结构的内存屏障指令表。

答案 1 :(得分:3)

给出代码:

#include <atomic>

static std::atomic<long> gt;

void test1() {
    gt.store(41, std::memory_order_release);
    gt.store(42, std::memory_order_relaxed);
    gt.load(std::memory_order_acquire);
    gt.load(std::memory_order_relaxed);
}

在体面的优化级别上,没有垃圾程序集在寄存器上移动值比堆栈大:

test1():
        movq    $41, gt(%rip)
        movq    $42, gt(%rip)
        movq    gt(%rip), %rax
        movq    gt(%rip), %rax
        ret

我们看到,为不同的内存顺序生成了完全相同的代码;尽管按顺序在同一函数中测试不同的指令是非常不好的做法,因为C ++指令不必独立编译,上下文可能会影响代码生成。但是,随着当前GCC中的代码生成,它会将涉及原子的每个语句编译为自己的语句。优良作法是对每个语句使用不同的功能。

此处生成相同的代码,因为这些存储顺序不需要特殊指令