atomic_thread_fence(memory_order_seq_cst)是否具有完整内存屏障的语义?

时间:2014-08-25 01:47:28

标签: c++ c++11

完整/通用内存屏障是屏障之前指定的所有LOAD和STORE操作似乎在屏障之后指定的所有LOAD和STORE操作之前相对于系统的其他组件发生的屏障。

根据cppreferencememory_order_seq_cst等于memory_order_acq_rel加上所有已标记的操作的总修改顺序。但据我所知,C ++ 11中的获取和释放栏都没有强制执行#StoreLoad(加载后存储)排序。发布围栏要求以后的任何写入都不能重新排序先前的读/写;获取围栏要求不能对任何先前的读取重新排序后续读/写。如果我错了,请纠正我;)

举个例子,

atomic<int> x;
atomic<int> y;

y.store(1, memory_order_relaxed);            //(1)
atomic_thread_fence(memory_order_seq_cst);   //(2)
x.load(memory_order_relaxed);                //(3)

优化编译器是否允许将指令(3)重新排序到(1)之前,以使其有效看起来像:

x.load(memory_order_relaxed);                //(3)
y.store(1, memory_order_relaxed);            //(1)
atomic_thread_fence(memory_order_seq_cst);   //(2)

如果这是一个有效的转换,那么它证明atomic_thread_fence(memory_order_seq_cst)并不一定包含完整障碍所具有的语义。

3 个答案:

答案 0 :(得分:2)

atomic_thread_fence(memory_order_seq_cst)始终会产生全屏障。

  • x86_64:MFENCE
  • PowerPC:hwsync
  • Itanuim:mf
  • ARMv7 / ARMv8:dmb ish
  • MIPS64:sync

主要的事情:观察线程可以简单地以不同的顺序观察,并且在观察到的线程中使用的是什么围栏并不重要。

  

优化编译器是否允许将指令(3)重新排序为   之前(1)?

不,不允许。但是在全局可见的多线程程序中,这是真的,只有在:

  • 其他线程对这些值使用相同的memory_order_seq_cst进行原子读/写操作
  • 或者其他线程在load()和store()之间使用相同的atomic_thread_fence(memory_order_seq_cst); - 但这种方法一般不保证顺序一致性,因为顺序一致性是更强有力的保证

工作草案,编程语言C ++标准2016-07-12:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/n4606.pdf

  

§29.3顺序和一致性

     

§29.3/ 8

     

[注意:memory_order_seq_cst仅确保a的顺序一致性   没有数据竞赛且仅使用的程序   memory_order_seq_cst操作。任何使用较弱的订购都会   除非使用极其谨慎,否则此保证无效。特别是,   memory_order_seq_cst围栏确保仅针对围栏的总订单   他们自己。 通常,挡板不能用于恢复顺序   具有较弱订购规范的原子操作的一致性。    - 结束说明]

如何映射到汇编程序:

<强>案例-1:

atomic<int> x, y

y.store(1, memory_order_relaxed);            //(1)
atomic_thread_fence(memory_order_seq_cst);   //(2)
x.load(memory_order_relaxed);                //(3)

此代码 总是等同于Case-2的含义,但此代码在STORE&amp;之间产生相同的指令。 LOAD,以及LOAD和STORE都使用memory_order_seq_cst - 这是顺序一致性,阻止StoreLoad重新排序,案例-2

atomic<int> x, y;

y.store(1, memory_order_seq_cst);            //(1)

x.load(memory_order_seq_cst);                //(3)

有一些注意事项:

  1. 它可能会添加重复的指令(如以下MIPS64示例所示)
  2. 或者可以使用其他说明形式的类似操作:

  3.   

    ARMv8-A指南

         

    表13.1。屏障参数

         

    ISH任意 - 任何

         

    Any - Any这意味着加载和存储必须在之前完成   障碍。屏障后出现的加载和存储   程序订单必须等待屏障完成。

    可以通过这两个指令之间的附加指令来防止重新排序两条指令。当我们看到第一个STORE(seq_cst)和下一个LOAD(seq_cst)之间生成的指令与FENCE(seq_cst)相同atomic_thread_fence(memory_order_seq_cst)

    将C / C ++ 11 memory_order_seq_cst映射到不同的CPU架构:load()store()atomic_thread_fence()

    注意atomic_thread_fence(memory_order_seq_cst); 始终生成全屏障

    • x86_64:商店 - MOV (into memory),MFENCE,加载 - MOV (from memory),围栏 - MFENCE

    • x86_64-alt:商店 - MOV (into memory),LOAD- MFENCE ,MOV (from memory),围栏 - MFENCE

      < / LI>
    • x86_64-alt3:商店 - (LOCK) XCHG,负载 - MOV (from memory),围栏 - MFENCE - 完全屏障

    • x86_64-alt4:商店 - MOV (into memory),负载 - LOCK XADD(0),围栏 - MFENCE - 全屏障

    • PowerPC:商店 - hwsync; st,加载 - hwsync; ld; cmp; bc; isync,围栏 - hwsync

    • Itanium:STORE - st.rel; mf ,LOAD - ld.acq,fence - mf

    • ARMv7:商店 - dmb ish; str; dmb ish ,加载 - ldr; dmb ish,栅栏 - dmb ish

    • ARMv7-alt:STORE - dmb ish; str,LOAD- dmb ish; ldr; dmb ish,围栏 - dmb ish

      < / LI>
    • ARMv8(AArch32):商店 - STL,负载 - LDA,围栏 - DMB ISH - 全屏障

    • ARMv8(AArch64):商店 - STLR,负载 - LDAR,围栏 - DMB ISH - 完全屏障

    • MIPS64:商店 - sync; sw;sync;,加载 - sync; lw; sync;,围栏 - sync

    描述了C / C ++ 11语义到不同CPU架构的所有映射:load(),store(),atomic_thread_fence():http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html

    由于顺序一致性会阻止StoreLoad重新排序,并且由于顺序一致性(store(memory_order_seq_cst)和下一个load(memory_order_seq_cst))生成的指令与atomic_thread_fence(memory_order_seq_cst)相同,因此{{1}阻止StoreLoad重新排序。

答案 1 :(得分:0)

C ++ fences不是CPU fence指令的直接等价物,尽管它们可能就是这样实现的。 C ++ fences是C ++内存模型的一部分,它完全是关于可见性和排序约束的。

鉴于处理器通常会对读取和写入进行重新排序,并在将值提供给其他核心或处理器之前将其缓存在本地,因此其他处理器可以看到效果的顺序通常是不可预测的。

在考虑这些语义时,重要的是要考虑一下你试图阻止的内容。

假设代码映射到机器指令,如(1),然后(2)然后(3),这些指令保证(1)在执行(3)之前全局可见。

该片段的整个目的是与另一个线程进行通信。在此代码段在我们的处理器上执行时,您无法保证其他线程在任何处理器上运行。因此,整个片段可以不间断地运行,并且(3)在执行(1)时仍将读取x中的任何值。在这种情况下,它与(3)(1)(2)的执行顺序无法区分。

所以:是的,这是一个允许的优化,因为你无法分辨出来。

答案 2 :(得分:0)

根据赫伯·萨特(Herb Sutter)的talk(请参见时间45:00),std::memory_order_seq_cst将强制执行StoreLoad,与std::memory_order_acq_rel不同。