C++中变量的修改顺序是如何定义的?
How is modification order of a variable defined in C++?
我已阅读此问答:
作者概述了一个有趣的评估,该评估在 C++20 之前是不可能的,但显然从 C++20 开始是可能的:
.-- 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); <-' | (x)
| | : | 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
订单(为了方便我添加了(x);这条线不参与SC订单,因为它不是SC操作)。
我试图理解这个例子中y
的mod化顺序是什么,但我不知道如何确定它。 (或者此评估是否有多个可能的 mody
化顺序?..)
更一般地说,modC++ 中原子变量的化序是如何定义的?
例如有这个:https://en.cppreference.com/w/cpp/atomic/memory_order
Write-write coherence: If evaluation A that modifies some atomic M (a write) happens-before evaluation B that modifies M, then A appears earlier than B in the modification order of M
所以看来mod化顺序必须与先写先行一致。
它是唯一定义mod化顺序的东西吗?
在上面的例子中,AFAIU (2) 和 (1) 之间没有 happens-before ;那么在 y
的 mod 化顺序中哪个是第一个?
mod的顺序是y
(x 1 2)(本次评价)吗?
我相信它也可能有助于推理 seq_cst
顺序...
对象的mod化顺序是线程在紧密循环运行 while(1) { y.load(relaxed); }
中旋转时看到的顺序,并且碰巧看到每一个变化。 (这并不是实际观察它的有用方法,但每个对象都有自己的 mod 所有线程都可以始终同意的化序,就像在真正的 CPU 上一样,因为需要 MESI 独占所有权才能将存储提交给 L1d缓存。或者通过 POWER CPU 上的 SMT 线程之间的私有 store-forwarding 尽早查看它。)
一些随机事实:
- 单个对象的mod化顺序与一个线程内的程序顺序兼容,即使
relaxed
- 线程看到一个带负载的值后,即使加载和存储是
relaxed
.[=32,它也只能看到那个值或 modification order 后面的值=]
- 观察到的变量值的变化次数不能超过其他线程存储的次数。如果你有一堆来自一堆线程的商店,其中一个将是最后一个,这就是所有 readers 将看到的值(最终)。在尘埃落定的同时,任何给定的 reader 都不会看到值来回变化,除了实际看到 mod 订单中后面的商店。 (参见 [intro.races] in the standard 中的“一致性”规则)
我认为此评估显示的是实际有效顺序,因此 y
的 mod 顺序只是从上到下阅读,2 x 1
。 (因为它使用了足够多的 seq_cst 操作,所有线程都可以就顺序达成一致,并表明其他一些东西最终在 seq_cst 存储 (2) 之后对发布存储 (x) 进行排序。)
这个评估命令是说 (2) 商店确实在 (x) 商店之前变得可见,所以 (x) 商店取代了它。
尘埃落定在 y.fetch_add
(1) 之前,否则它会有 synced-with (2) 而不是 (x)。
我已阅读此问答:
作者概述了一个有趣的评估,该评估在 C++20 之前是不可能的,但显然从 C++20 开始是可能的:
.-- 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); <-' | (x)
| | : | 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
订单(为了方便我添加了(x);这条线不参与SC订单,因为它不是SC操作)。
我试图理解这个例子中y
的mod化顺序是什么,但我不知道如何确定它。 (或者此评估是否有多个可能的 mody
化顺序?..)
更一般地说,modC++ 中原子变量的化序是如何定义的? 例如有这个:https://en.cppreference.com/w/cpp/atomic/memory_order
Write-write coherence: If evaluation A that modifies some atomic M (a write) happens-before evaluation B that modifies M, then A appears earlier than B in the modification order of M
所以看来mod化顺序必须与先写先行一致。
它是唯一定义mod化顺序的东西吗?
在上面的例子中,AFAIU (2) 和 (1) 之间没有 happens-before ;那么在 y
的 mod 化顺序中哪个是第一个?
mod的顺序是y
(x 1 2)(本次评价)吗?
我相信它也可能有助于推理 seq_cst
顺序...
对象的mod化顺序是线程在紧密循环运行 while(1) { y.load(relaxed); }
中旋转时看到的顺序,并且碰巧看到每一个变化。 (这并不是实际观察它的有用方法,但每个对象都有自己的 mod 所有线程都可以始终同意的化序,就像在真正的 CPU 上一样,因为需要 MESI 独占所有权才能将存储提交给 L1d缓存。或者通过 POWER CPU 上的 SMT 线程之间的私有 store-forwarding 尽早查看它。)
一些随机事实:
- 单个对象的mod化顺序与一个线程内的程序顺序兼容,即使
relaxed
- 线程看到一个带负载的值后,即使加载和存储是
relaxed
.[=32,它也只能看到那个值或 modification order 后面的值=] - 观察到的变量值的变化次数不能超过其他线程存储的次数。如果你有一堆来自一堆线程的商店,其中一个将是最后一个,这就是所有 readers 将看到的值(最终)。在尘埃落定的同时,任何给定的 reader 都不会看到值来回变化,除了实际看到 mod 订单中后面的商店。 (参见 [intro.races] in the standard 中的“一致性”规则)
我认为此评估显示的是实际有效顺序,因此 y
的 mod 顺序只是从上到下阅读,2 x 1
。 (因为它使用了足够多的 seq_cst 操作,所有线程都可以就顺序达成一致,并表明其他一些东西最终在 seq_cst 存储 (2) 之后对发布存储 (x) 进行排序。)
这个评估命令是说 (2) 商店确实在 (x) 商店之前变得可见,所以 (x) 商店取代了它。
尘埃落定在 y.fetch_add
(1) 之前,否则它会有 synced-with (2) 而不是 (x)。