C++ 放松的原子规则(轻微)有什么不同?

C++ 放松的原子规则(轻微)有什么不同?,c++,memory-barriers,memory-model,stdatomic,relaxed-atomics,C++,Memory Barriers,Memory Model,Stdatomic,Relaxed Atomics,看到赫伯·萨特尔斯(Herb Sutters)在“原子武器”方面的出色表现后,我对轻松原子学的例子有点困惑 我跟我说,C++内存模型中的一个原子(SC-DRF =顺序一致的无数据竞争)在加载/读取上获得“获取”。 我理解,对于加载[和存储]而言,默认值是std::memory\u order\u seq\u cst,因此两者是相同的: myatomic.load(); // (1) myatomic.load(std::memory_order_

看到赫伯·萨特尔斯(Herb Sutters)在“原子武器”方面的出色表现后,我对轻松原子学的例子有点困惑

我跟我说,C++内存模型中的一个原子(SC-DRF =顺序一致的无数据竞争)在加载/读取上获得“获取”。 我理解,对于加载[和存储]而言,默认值是

std::memory\u order\u seq\u cst
,因此两者是相同的:

myatomic.load();                          // (1)
myatomic.load(std::memory_order_seq_cst); // (2)
到目前为止还不错,没有涉及到放松原子(听了演讲后,我将永远不会使用放松原子。永远。承诺。但当有人问我时,我可能不得不解释…)

但是为什么我使用“放松”语义呢

myatomic.load(std::memory_order_acquire);   // (3)
既然加载获取不释放,为什么这与
(1)
(2)
不同这里真正放松的是什么?

我唯一能想到的是我误解了负载意味着获得。如果这是真的,那么默认的
seq_cst
意味着两者,这不是意味着一个完整的栅栏吗?没有任何东西可以通过该指令,也没有任何东西可以通过该指令?我一定是误解了那一部分


[存储和释放对称]。

调用
myatomic.load(std::memory\u order\u acquire)可能会有点混乱一个“松弛的原子”负载,因为存在一个
std::memory\u order\u relaxed
。有些人将任何比cst弱的订单描述为“放松”

您可以正确地注意到,顺序一致性加载是一个获取加载,但它还有一个额外的要求:顺序一致性加载也是所有seq_cst操作的总全局顺序的一部分

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

如果这是真的,默认的
seq_cst
意味着两者,不是吗 我指的是满满的篱笆

这绝对不是指两者或“完全隔离”的意思

seq_cst

  • 仅获取加载操作时的
  • 并仅在门店运营时发布
因此,它仅在结合两者的操作上暗示了两者:RMW原子操作

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

原子对象上的
seq_cst
操作的目的不是提供一个使所有其他内存操作有序的“围栏”。

如果您“放松”seq_cst的一些排序要求,则有
mou acq_rel
(以及纯获取和纯释放)

比这更放松的是
mou-relaxed
;没有订购说明。还有别的吗,只是原子性

为大多数ISA编译时,seq_cst加载可以使用与acquire加载相同的asm;我们选择使存储变得昂贵,而不是负载。对于包括x86、POWER、ARMv7在内的ISA,ARMv8为某些ISA提供了两种替代方案。为了彼此兼容,相同平台的编译器必须选择相同的策略,否则一个函数中的seq_cst存储可能会与另一个函数中的seq_cst加载重新排序

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

这是一个在不同变量上创建全局总操作顺序的实际示例,所有线程都可以同意该操作顺序。(x86 asm为纯存储提供纯加载/释放获取,或为
锁提供seq_cst
-前缀原子RMW指令。因此,Preshing的x86 asm示例与C++11
mo_释放
存储完全对应,而不是
mo_seq_cst


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

等待刷新只能在执行LDAR之前发生;其他加载可以执行,甚至更晚的存储可以提交到L1d。()。要做到这一点,LDAR必须探测存储缓冲区以检查STLR存储。但是,如果您能够做到这一点,
mo_seq_cst
存储区可能比x86上的存储区要便宜得多,前提是您不立即按顺序加载任何其他内容

在大多数其他ISA上,恢复顺序一致性的唯一选项是全屏障指令(存储后)这会阻止所有的后续负载和存储,直到所有先前的存储都提交到L1D缓存。但是这不是ISO C++ >代码> SEQJCST < /C>所暗示或要求的,只是AAGCH64具有强大的能力,如ISO C++所要求的,但没有更强的。 (为许多其他弱有序的ISA进行编译时,需要将acq/发布功能提升到比所需功能更强的级别,例如,ARMv7需要为发布存储设置一个完整的屏障。)