与“(简单地)之前发生”相比,'strongly happens before' 的意义是什么?
What is the significance of 'strongly happens before' compared to '(simply) happens before'?
该标准定义了几个 'happens before' 关系,它们将旧的 'sequenced before' 扩展到多个线程:
[intro.races]
11 An evaluation A simply happens before an evaluation B if either
(11.1) — A is sequenced before B, or
(11.2) — A synchronizes with B, or
(11.3) — A simply happens before X and X simply happens before B.
[Note 10: In the absence of consume operations, the happens before and simply happens before relations are identical. — end note]
12 An evaluation A strongly happens before an evaluation D if, either
(12.1) — A is sequenced before D, or
(12.2) — A synchronizes with D, and both A and D are sequentially consistent atomic operations
([atomics.order]), or
(12.3) — there are evaluations B and C such that A is sequenced before B, B simply happens before C, and C is sequenced before D, or
(12.4) — there is an evaluation B such that A strongly happens before B, and B strongly happens before D.
[Note 11: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. — end note]
(大胆的矿)
两者之间的区别似乎非常微妙。 'Strongly happens before' 对于匹配对或发布-获取操作永远不会成立(除非两者都是 seq-cst),但它仍然在某种程度上尊重发布-获取同步,因为操作顺序在发布之前 'strongly happen before' 在匹配获取之后排序的操作。
为什么这种差异很重要?
'Strongly happens before'是在C++20引入的,在C++20之前,'simply happens before'曾经被称为'strongly happens before'。为什么引入它?
[atomics.order]/4
表示所有seq-cst操作的总顺序与'strongly happens before'.
一致
是不是和'simply happens before'不一致?如果是,为什么不呢?
我忽略了普通的 'happens before',因为它与 'simply happens before' 的不同之处仅在于它对 memory_order_consume
的处理,temporarily discouraged 的使用,因为显然大多数(全部?)主要编译器将其视为 memory_order_acquire
.
我已经看到了 this Q&A,但是它并没有解释为什么 'strongly happens before' 存在,也没有完全说明它的含义(它只是声明它不尊重发布-获得同步,但情况并非完全如此。
找到 the proposal 介绍 'simply happens before'.
我不是很明白,但是解释如下:
- 'Strongly happens before'是'simply happens before'的弱化版本。
- 只有当 seq-cst 与 aqc-rel 在同一变量上混合时才能观察到差异(我认为,这意味着当获取负载从 seq-cst 存储中读取值时,或者当 seq-cst load 从发布存储中读取一个值)。但是我还不清楚混合两者的确切效果。
这是我目前的理解,可能不完整或不正确。验证将不胜感激。
C++20 将 strongly happens before
重命名为 simply happens before
,并为 strongly happens before
引入了一个新的、更宽松的定义,减少了顺序。
Simply happens before
用于推断代码中是否存在数据竞争。 (实际上那将是普通的 'happens before',但是在没有使用操作的情况下两者是等价的,标准不鼓励使用它,因为大多数(所有?)主要编译器将它们视为获取。)
较弱的strongly happens before
用于推理seq-cst操作的全局顺序
Lahav 等人在提议 P0668R5: Revising the C++ memory model, which is based on the paper Repairing Sequential Consistency in C/C++11 中引入了此更改(我没有完全阅读)。
提案解释了进行更改的原因。长话短说,大多数编译器在 Power 和 ARM 架构上实现原子的方式在极少数情况下被证明是不一致的,修复编译器会产生性能成本,因此他们修复了标准。
如果您在同一个原子变量上将 seq-cst 操作与获取-释放操作混合使用,更改只会影响您(即,如果获取操作从 seq- cst 存储,或 seq-cst 操作从发布存储读取值)。
如果您不以这种方式混合操作,那么您不会受到影响(即可以将 simply happens before
和 strongly happens before
视为等效)。
变化的要点是一个seq-cst操作和相应的acquire/release操作之间的同步不再影响这个特定的seq-cst操作在全局seq中的位置-cst 命令,但同步本身仍然存在。
这使得此类 seq-cst 操作的 seq-cst 顺序非常没有实际意义,请参见下文。
该提案给出了以下示例,我将尝试解释我对它的理解:
atomic_int x = 0, y = 0;
int a = 0, b = 0, c = 0;
// Thread 1
x.store(1, seq_cst);
y.store(1, release);
// Thread 2
b = y.fetch_add(1, seq_cst); // b = 1 (the value of y before increment)
c = y.load(relaxed); // c = 3
// Thread 3
y.store(3, seq_cst);
a = x.load(seq_cst); // a = 0
评论指出了此代码可以执行的方式之一,标准曾经禁止(在此更改之前),但实际上可以在受影响的体系结构上发生。
执行过程如下:
.-- T3 y.store(3, seq_cst); --. (2)
| | | strongly
| | sequenced before | happens
| V | before
| T3 a = x.load(seq_cst); // a = 0 --. <-' (3)
| : coherence-
| : ordered
| : before
| T1 x.store(1, seq_cst); <-' --. --. (4)
| | |st |
| | sequenced before |h |
| V |b |
| . T1 y.store(1, release); <-' |
| | : | strongly
| | : synchronizes with | happens
| | V | before
| > T2 b = y.fetch_add(1, seq_cst); // b = 1 --. | (1)
| | |st |
| | sequenced before |h |
| V |b |
'-> T2 c = y.load(relaxed); // c = 3 <-' <-'
其中:
右边括号中的数字表示全局 seq-cst 顺序。
左侧的箭头显示值如何在某些加载和存储之间传播。
中间的箭头显示:
- 'Sequenced before',好的旧单线程求值顺序。
- 'Synchronizes with',release-acquire同步(seq-cst loads算作acquire操作,seq-cst stores算作release操作)
这两个加起来就是'simply happens before'.
右边的箭头是以中间的箭头为基础的,表示:
新定义的'strongly happens before'关系。
'Coherence-ordered before',本提案中引入的新关系,仅用于定义全局 seq-cst 顺序,显然不强加同步(与发布获取操作不同)。
似乎它包括除 'simply happens before' 以外的所有影响全局 seq-cst 顺序的内容。在这种情况下,这是常识,如果加载没有看到存储写入的值,那么加载先于存储。
全局seq-cst顺序与两者一致
请注意,在这张图片上,在 b = y.fetch_add(1, seq_cst);
之前没有发生任何强烈的事情,所以在全局 seq-cst 顺序中没有任何必须在它之前的东西,所以 可以移动它一直到 seq-cst 顺序的开头,这就是这种情况最终发生的情况,即使它读取了后来(按此顺序)操作产生的值。
该标准定义了几个 'happens before' 关系,它们将旧的 'sequenced before' 扩展到多个线程:
[intro.races]
11 An evaluation A simply happens before an evaluation B if either
(11.1) — A is sequenced before B, or
(11.2) — A synchronizes with B, or
(11.3) — A simply happens before X and X simply happens before B.[Note 10: In the absence of consume operations, the happens before and simply happens before relations are identical. — end note]
12 An evaluation A strongly happens before an evaluation D if, either
(12.1) — A is sequenced before D, or
(12.2) — A synchronizes with D, and both A and D are sequentially consistent atomic operations ([atomics.order]), or
(12.3) — there are evaluations B and C such that A is sequenced before B, B simply happens before C, and C is sequenced before D, or
(12.4) — there is an evaluation B such that A strongly happens before B, and B strongly happens before D.[Note 11: Informally, if A strongly happens before B, then A appears to be evaluated before B in all contexts. Strongly happens before excludes consume operations. — end note]
(大胆的矿)
两者之间的区别似乎非常微妙。 'Strongly happens before' 对于匹配对或发布-获取操作永远不会成立(除非两者都是 seq-cst),但它仍然在某种程度上尊重发布-获取同步,因为操作顺序在发布之前 'strongly happen before' 在匹配获取之后排序的操作。
为什么这种差异很重要?
'Strongly happens before'是在C++20引入的,在C++20之前,'simply happens before'曾经被称为'strongly happens before'。为什么引入它?
[atomics.order]/4
表示所有seq-cst操作的总顺序与'strongly happens before'.
是不是和'simply happens before'不一致?如果是,为什么不呢?
我忽略了普通的 'happens before',因为它与 'simply happens before' 的不同之处仅在于它对 memory_order_consume
的处理,temporarily discouraged 的使用,因为显然大多数(全部?)主要编译器将其视为 memory_order_acquire
.
我已经看到了 this Q&A,但是它并没有解释为什么 'strongly happens before' 存在,也没有完全说明它的含义(它只是声明它不尊重发布-获得同步,但情况并非完全如此。
找到 the proposal 介绍 'simply happens before'.
我不是很明白,但是解释如下:
- 'Strongly happens before'是'simply happens before'的弱化版本。
- 只有当 seq-cst 与 aqc-rel 在同一变量上混合时才能观察到差异(我认为,这意味着当获取负载从 seq-cst 存储中读取值时,或者当 seq-cst load 从发布存储中读取一个值)。但是我还不清楚混合两者的确切效果。
这是我目前的理解,可能不完整或不正确。验证将不胜感激。
C++20 将 strongly happens before
重命名为 simply happens before
,并为 strongly happens before
引入了一个新的、更宽松的定义,减少了顺序。
Simply happens before
用于推断代码中是否存在数据竞争。 (实际上那将是普通的 'happens before',但是在没有使用操作的情况下两者是等价的,标准不鼓励使用它,因为大多数(所有?)主要编译器将它们视为获取。)
较弱的strongly happens before
用于推理seq-cst操作的全局顺序
Lahav 等人在提议 P0668R5: Revising the C++ memory model, which is based on the paper Repairing Sequential Consistency in C/C++11 中引入了此更改(我没有完全阅读)。
提案解释了进行更改的原因。长话短说,大多数编译器在 Power 和 ARM 架构上实现原子的方式在极少数情况下被证明是不一致的,修复编译器会产生性能成本,因此他们修复了标准。
如果您在同一个原子变量上将 seq-cst 操作与获取-释放操作混合使用,更改只会影响您(即,如果获取操作从 seq- cst 存储,或 seq-cst 操作从发布存储读取值)。
如果您不以这种方式混合操作,那么您不会受到影响(即可以将 simply happens before
和 strongly happens before
视为等效)。
变化的要点是一个seq-cst操作和相应的acquire/release操作之间的同步不再影响这个特定的seq-cst操作在全局seq中的位置-cst 命令,但同步本身仍然存在。
这使得此类 seq-cst 操作的 seq-cst 顺序非常没有实际意义,请参见下文。
该提案给出了以下示例,我将尝试解释我对它的理解:
atomic_int x = 0, y = 0;
int a = 0, b = 0, c = 0;
// Thread 1
x.store(1, seq_cst);
y.store(1, release);
// Thread 2
b = y.fetch_add(1, seq_cst); // b = 1 (the value of y before increment)
c = y.load(relaxed); // c = 3
// Thread 3
y.store(3, seq_cst);
a = x.load(seq_cst); // a = 0
评论指出了此代码可以执行的方式之一,标准曾经禁止(在此更改之前),但实际上可以在受影响的体系结构上发生。
执行过程如下:
.-- T3 y.store(3, seq_cst); --. (2)
| | | strongly
| | sequenced before | happens
| V | before
| T3 a = x.load(seq_cst); // a = 0 --. <-' (3)
| : coherence-
| : ordered
| : before
| T1 x.store(1, seq_cst); <-' --. --. (4)
| | |st |
| | sequenced before |h |
| V |b |
| . T1 y.store(1, release); <-' |
| | : | strongly
| | : synchronizes with | happens
| | V | before
| > T2 b = y.fetch_add(1, seq_cst); // b = 1 --. | (1)
| | |st |
| | sequenced before |h |
| V |b |
'-> T2 c = y.load(relaxed); // c = 3 <-' <-'
其中:
右边括号中的数字表示全局 seq-cst 顺序。
左侧的箭头显示值如何在某些加载和存储之间传播。
中间的箭头显示:
- 'Sequenced before',好的旧单线程求值顺序。
- 'Synchronizes with',release-acquire同步(seq-cst loads算作acquire操作,seq-cst stores算作release操作)
这两个加起来就是'simply happens before'.
右边的箭头是以中间的箭头为基础的,表示:
新定义的'strongly happens before'关系。
'Coherence-ordered before',本提案中引入的新关系,仅用于定义全局 seq-cst 顺序,显然不强加同步(与发布获取操作不同)。
似乎它包括除 'simply happens before' 以外的所有影响全局 seq-cst 顺序的内容。在这种情况下,这是常识,如果加载没有看到存储写入的值,那么加载先于存储。
全局seq-cst顺序与两者一致
请注意,在这张图片上,在 b = y.fetch_add(1, seq_cst);
之前没有发生任何强烈的事情,所以在全局 seq-cst 顺序中没有任何必须在它之前的东西,所以 可以移动它一直到 seq-cst 顺序的开头,这就是这种情况最终发生的情况,即使它读取了后来(按此顺序)操作产生的值。