为什么逻辑 AND/OR 的左操作数不依赖于父计算?

Lif*_*ang 4 c++ atomic lock-free carries-dependency stdatomic

根据C++标准:

\n
\n

如果 A 的值用作 B 的操作数,则求值 A 具有对求值 B 的依赖性,除非:

\n

\xe2\x80\x94 B 是 std::kill_dependency (29.3) 的任何特化的调用,或者

\n

\xe2\x80\x94 A 是内置逻辑 AND(&&,参见 5.14)或逻辑 OR(||,参见 5.15)运算符的左操作数,或者

\n

\xe2\x80\x94 A 是条件(?:,参见 5.16)运算符的左操作数,或者

\n

\xe2\x80\x94 A 是内置逗号 (,) 运算符的左操作数 (5.18);(...)

\n
\n

我可以理解为什么关系之前排序的依赖项会在kill_dependency调用时停止,但是为什么逻辑AND、OR、逗号等运算符也会破坏依赖链?

\n

这是否意味着下面的代码有未定义的行为?

\n
//thread1\nint y = 2\natomicVal.store(true);\n\n//thread2 \nauto x = atomicVal.load(std::memory_order_consume);\ncout << x && y;\n
Run Code Online (Sandbox Code Playgroud)\n

Pet*_*des 5

memory_order_consume是一次尝试公开 asm 级 CPU 功能以在 C++ 中使用。(它暂时被弃用,直到它可以被重新设计为编译器可以在实践中实现的东西,并且不需要kill_dependency源代码中有太多噪音)。了解 CPU 行为是理解旨在公开它的 C++ 内容设计的关键。

这都是关于数据依赖关系,而不是像条件分支这样的控制依赖关系。 C++11:memory_order_relaxed 和 memory_order_consume 之间的区别以及[[carries_dependency]] 的含义以及如何实现有更多细节。

例如,一条add x2, x2, x3指令在其两个输入寄存器都准备好之前无法执行,并且ldr w1, [x2]在地址准备好之前都无法执行加载,因此如果x2来自另一个加载,它将自动排序在该加载之前。(假设 CPU 硬件的设计不违反因果关系,例如通过进行值预测或在极少数情况下 DEC Alpha 所做的任何违反因果关系的事情)。但可以预测,因此仅仅等待产生 w1 的负载cbz w1, reg_was_zero是不够的。reg_was_zero: ldr w3, [x4](顺便说一句,这是 AArch64 asm,一个保证依赖顺序的弱有序 ISA。)

||or的短路求值left && right在逻辑上与 an 相同if(left) right,因此即使左侧尚未执行,分支预测 + 推测执行也有望运行右侧。 没有数据依赖性,只有控制依赖性。

显然,逗号left, right不会在两边之间建立任何联系,它基本上是一种塞入left; right;单个表达式的方法。

当然,如果您在左侧和右侧使用相同的变量,则可以以这种方式存在数据依赖性,但它不是由运算符创建的。