9

完整/通用内存屏障是指在屏障之前指定的所有 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)不一定包含完整屏障所具有的语义。

4

3 回答 3

7

atomic_thread_fence(memory_order_seq_cst)总是产生一个完整的障碍。

  • x86_64:MFENCE
  • 电源电脑:hwsync
  • 伊塔努伊姆:mf
  • ARMv7 / ARMv8:dmb ish
  • MIPS64:sync

主要的事情:观察线程可以简单地以不同的顺序观察,与你在观察线程中使用什么栅栏无关。

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

不,这是不允许的。但是对于多线程程序来说,这是全局可见的,只有在以下情况下才是正确的:

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

工作草案,编程语言 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 仅确保没有数据竞争且仅使用 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 和 LOAD 之间以及 LOAD 和 STORE 都使用时产生相同的指令memory_order_seq_cst- 这是防止 StoreLoad-reordering 的顺序一致性,Case-2

atomic<int> x, y;

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

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

附上一些注释:

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

    • 与 x86_64 的替代 3/4 映射一样,-prefixLOCK完全刷新 Store-BufferMFENCE以防止 StoreLoad 重新排序
    • 或 ARMv8 - 我们知道,这DMB ISH是阻止 StoreLoad 重新排序的全屏障:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0024a/ CHDGACJD.html

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++11memory_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),加载-,围栏-MFENCE,MOV (from memory)MFENCE

  • x86_64-alt3: STORE- (LOCK) XCHG, LOAD- MOV (from memory), fence- MFENCE-全屏障

  • x86_64-alt4: STORE- MOV (into memory), LOAD- LOCK XADD(0), fence- MFENCE-全屏障

  • PowerPC: STORE- hwsync; st、 LOAD- 、 fence-hwsync;ld; cmp; bc; isynchwsync

  • 安腾:STORE- st.rel;mf、 LOAD- ld.acq、 fence-mf

  • ARMv7:dmb ish; str;dmb ish存储- ,加载- ldr; dmb ish,围栏-dmb ish

  • ARMv7-alt:存储- dmb ish; str、加载-、围栏-dmb ish;ldr; dmb ishdmb ish

  • ARMv8(AArch32): STORE- STL, LOAD- LDA, fence- DMB ISH-全屏障

  • ARMv8(AArch64): STORE- STLR, LOAD- LDAR, fence- DMB ISH-全屏障

  • MIPS64: STORE- sync; sw;sync;、 LOAD- sync; lw; sync;、 fence-sync

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

因为 Sequential-Consistency 阻止了 StoreLoad-reordering,并且因为 Sequential-Consistency (store(memory_order_seq_cst)和 next load(memory_order_seq_cst)) 在其之间生成的指令与 相同atomic_thread_fence(memory_order_seq_cst),所以atomic_thread_fence(memory_order_seq_cst)阻止了 StoreLoad-reordering。

于 2016-08-20T16:50:06.253 回答
0

C++ 栅栏不是 CPU 栅栏指令的直接等价物,尽管它们很可能是这样实现的。C++ 栅栏是 C++ 内存模型的一部分,它与可见性和排序约束有关。

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

因此,在考虑这些语义时,重要的是要考虑您要阻止的内容是什么。

让我们假设代码被映射到编写的机器指令,(1)然后(2)然后(3),并且这些指令保证(1)在执行(3)之前是全局可见的。

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

所以:是的,这是允许的优化,因为你无法区分。

于 2014-10-02T13:31:03.333 回答
0

根据 Herb Sutter 的谈话(见时间 45:00 左右),std::memory_order_seq_cst将强制执行 StoreLoad,与std::memory_order_acq_rel.

于 2020-02-26T22:48:46.810 回答