3

在看到 Herb Sutters关于“原子武器”的精彩演讲后,我对“轻松原子”示例感到有些困惑。

我认为C++ 内存模型中的原子(SC-DRF = Sequentially Consistent for Data Race Free)在加载/读取时执行“获取”。

我知道对于负载 [和商店],默认值是std::memory_order_seq_cst,因此两者是相同的:

myatomic.load();                          // (1)
myatomic.load(std::memory_order_seq_cst); // (2)

到目前为止一切顺利,没有涉及轻松原子(在听完演讲后,我永远不会使用轻松的原子。永远。承诺。但是当有人问我时,我可能不得不解释......)。

但是为什么当我使用时它是“宽松”的语义

myatomic.load(std::memory_order_acquire);   // (3)

既然负载获取不是释放,为什么这与(1)and不同(2)这里真正放松的是什么?

我唯一能想到的就是我误解了load的意思是acquire。如果这是真的,并且默认值seq_cst意味着两者,那是否意味着一个完整的围栏 - 没有什么可以传递该指令,也不能传递?我必须误解了那部分。

[并且对称地存储释放]。

4

3 回答 3

6

调用“宽松原子”负载可能会有点令人困惑myatomic.load(std::memory_order_acquire);,因为有一个std::memory_order_relaxed. 有些人将任何弱于“放松”的秩序描述seq_cst为“放松”。

您说得对,顺序一致的加载是一种获取加载,但它还有一个额外的要求:顺序一致的加载也是所有 seq_cst 操作的总全局顺序的一部分。

当您处理多个原子变量时,它就会发挥作用:两个原子的单独修改顺序可能以不同的相对顺序出现在不同的线程中,除非强加了顺序一致性。

于 2013-06-09T21:52:26.953 回答
2

如果您“放宽” seq_cst 的一些订购要求,就会有mo_acq_rel(以及纯获取和纯发布)。

比这还轻松mo_relaxed;没有订购。别的什么,只是原子性1

在为大多数ISA编译时,seq_cst 加载可以使用与获取加载相同的 asm;我们选择让商店变得昂贵,而不是负载。 C/C++11 映射到适用于 ISA 的处理器,包括 x86、POWER、ARMv7、ARMv8,包括一些 ISA 的 2 个替代方案。为了彼此兼容,同一平台的编译器必须选择相同的策略,否则一个函数中的 seq_cst 存储可能会随着另一个函数中的 seq_cst 加载而重新排序。

在内存模型包括存储缓冲区和一致缓存的典型 CPU 上,如果您在同一个线程中存储然后重新加载,则seq_cst 要求您在存储对所有线程全局可见之前不要让重新加载发生。这意味着在 seq_cst 存储之后或 seq_cst 加载之前的完整屏障(包括 StoreLoad )。由于廉价负载比廉价商店更有价值,例如,通常的映射选择 x86 mov+mfence商店。(同样适用于加载任何其他位置;在 store 提交之前不能这样做。这就是 Jeff Preshing 的Memory Reordering Caught in the Act的内容。)

这是在所有线程都可以同意的不同变量上创建全局总操作顺序的实际示例。(x86 asm 为 pure-store 的 pure-load / release 提供了获取,或为lock-prefixed atomic RMW 指令提供了 seq_cst。因此 Preshing 的 x86 asm 示例完全对应于 C++11mo_release存储而不是mo_seq_cst.


ARMv8 / AArch64 很有趣:它有 STLR(顺序发布存储)和 LDAR(获取加载)。与其在存储缓冲区耗尽并将 STLR 提交到 L1d 缓存(全局可见性)之前暂停所有后续加载,实现可以更有效。

等待刷新只需要在 LDAR 执行之前发生;其他加载可以执行,甚至以后的存储也可以提交到 L1d。(顺序释放仍然至少是一个单向障碍)。要做到如此高效/弱,LDAR 必须探测存储缓冲区以检查 STLR 存储。但是,如果你能做到这一点,mo_seq_cst如果你在那之后不立即执行 seq_cst 加载任何其他内容,那么存储空间可能会比 x86 上的便宜得多。

在大多数其他 ISA 上,恢复顺序一致性的唯一选择是完整的屏障指令(在存储之后)。这会阻止所有以后的加载和存储发生,直到所有先前的存储都提交到 L1d 缓存之后。但这不是ISO C++seq_cst所暗示或要求的,只是只有 AArch64 有能力与 ISO C++ 要求一样强大,但不会更强。

(为许多其他弱序 ISA 进行编译需要将 acq / release 提升到比需要的强得多,例如 ARMv7 需要一个完整的发布存储屏障。)


脚注 1:(就像你在旧的 C++11 之前的代码中得到的一样,使用自己的原子,volatile没有任何障碍)。

于 2019-12-03T03:16:58.107 回答
1

如果这是真的,并且默认seq_cst意味着两者,那不意味着一个完整的围栏

它绝对不意味着两者或“完整围栏”。

seq_cst暗示

  • 在加载操作时获取
  • 并且在商店运营时发布。

因此,它暗示将两者结合的操作:RMW 原子操作。

顺序一致性也意味着这些操作是全局有序的,即:seq_cst整个程序所标记的所有操作都按某种顺序运行,即与每个线程中操作的顺序相兼容的顺序。对于这些“顺序”操作,它没有说明其他原子操作的顺序。

seq_cst对原子对象进行操作的目的不是提供使所有其他内存操作按顺序进行的“栅栏”。

于 2019-12-02T05:42:23.410 回答