C++11 顺序一致性内存顺序是否禁止存储缓冲区石蕊测试?

Does C++11 sequential consistency memory order forbid store buffer litmus test?

考虑使用 SC 原子的存储缓冲区石蕊测试:

// Initial
std::atomic<int> x(0), y(0);

// Thread 1           // Thread 2
x.store(1);           y.store(1);
auto r1 = y.load();   auto r2 = x.load();

这个程序可以以 r1r2 都为零结束吗?

我看不出cppreference中关于memory_order_seq_cst的描述是如何禁止这个结果的:

A load operation with this memory order performs an acquire operation, a store performs a release operation, and read-modify-write performs both an acquire operation and a release operation, plus a single total order exists in which all threads observe all modifications in the same order

在我看来,memory_order_seq_cst就是acquire-release加上一个global store order。而且我不认为全球商店订单在这个特定的试金石中发挥作用。

SC 的 cppreference 摘要太弱,确实不够强,无法禁止重新排序。

在我看来,它所说的内容仅与 x86-TSO 一样强大(acq_rel 加上没有 IRIW 重新排序,即所有 reader 线程可以达成一致的总存储顺序)。

ISO C++ 实际上保证所有 SC 操作的总顺序包括加载(以及 SC 栅栏)与程序顺序一致。 (这基本上是 the standard definition of sequential consistency in computer science;仅使用 seq_cst 原子操作并且对于非原子访问没有数据争用的 C++ 程序按顺序一致地执行,即“恢复顺序一致性”,尽管允许完全优化非原子访问。)顺序一致性必须禁止同一线程中任何两个 SC 操作之间的任何重新排序,甚至是 StoreLoad 重新排序。

这意味着在每个 seq_cst 存储之后都会有一个昂贵的完整屏障(包括 StoreLoad),或者例如 AArch64 STLR / LDAR 无法 彼此 进行 StoreLoad 重新排序,但否则只能发布和获取 wrt。与其他操作重新排序。 (因此缓存命中 SC 存储在 AArch64 上比 x86 便宜很多,如果你之后不在同一线程中执行任何 SC 加载或 RMW 操作。)

参见 https://eel.is/c++draft/atomics.order#4 这清楚地表明 SC 操作不会重新排序。彼此。 当前标准草案 说:

31.4 [atomics.order]

  1. There is a single total order S on all memory_­order​::​seq_­cst operations, including fences, that satisfies the following constraints. First, if A and B are memory_­order​::​seq_­cst operations and A strongly happens before B, then A precedes B in S.

Second, for every pair of atomic operations A and B on an object M, where A is coherence-ordered before B, the following four conditions are required to be satisfied by S:

  • (4.1) if A and B are both memory_­order​::​seq_­cst operations, then A precedes B in S; and
  • (4.2 .. 4.4) - basically the same thing for sc fences wrt. operations.

Sequenced before意味着strongly happens before,所以开头的段落保证S与程序顺序一致。

4.1 是关于彼此 coherenced-ordered before/after 的操作。即恰好看到商店价值的负载。这将线程间可见性与总顺序 S 联系起来,使其与程序顺序相匹配。这两个要求的结合迫使编译器使用完整的屏障(包括 StoreLoad)来从它所针对的任何较弱的硬件模型中恢复顺序一致性。

(原来4.都是一段,我拆分是为了强调这里有两个独立的东西,一个是strongly-happens-before,ops/barriers的列表是连贯的-之前订购。)


这些保证,加上同步/发生之前,足以恢复整个程序的顺序一致性,如果它没有数据争用(那将是 UB),并且如果你不使用任何较弱的记忆顺序。

如果程序涉及较弱的订单,这些规则仍然有效,但例如两个 relaxed 操作之间的 SC 栅栏不如两个 SC 负载强。例如 仅使用 SC 操作的方式; IIRC PowerPC 在 SC 负载之前以及之后需要障碍。

所以有一些 SC 操作不一定足以恢复所有地方的顺序一致性;这就是使用较弱操作的意义所在,但其他操作可以重新排序 wrt 可能有点令人惊讶。 SC行动。 SC 行动不是 SC 围栏。另见 :将一家商店从 seq_cst 弱化到 release 允许重新排序。