为什么逻辑 AND/OR 的左操作数不依赖于父评估?
Why do left operands of logical AND/OR not carry dependency to the parent evaluation?
根据C++标准:
An evaluation A carries a dependency to an evaluation B if - the value of A is used as an operand of B, unless:
— B is an invocation of any specialization of std::kill_dependency (29.3), or
— A is the left operand of a built-in logical AND (&&, see 5.14) or logical OR (||, see 5.15) operator, or
— A is the left operand of a conditional (?:, see 5.16) operator, or
— A is the left operand of the built-in comma (,) operator (5.18); (...)
我能理解为什么在关系之前排序的依赖会在 kill_dependency 调用时停止,但为什么逻辑与、或、逗号等运算符也会破坏依赖链?
这是否意味着下面的代码有未定义的行为?
//thread1
int y = 2
atomicVal.store(true);
//thread2
auto x = atomicVal.load(std::memory_order_consume);
cout << x && y;
memory_order_consume
试图公开一个 asm 级别的 CPU 特性以供在 C++ 中使用。 (它是 直到它可以被重新设计为编译器可以在实践中实现的东西,并且不需要源代码中那么多 kill_dependency
噪音)。理解 CPU 行为是理解旨在公开它的 C++ 设计的关键。
都是关于数据的依赖,而不是像条件分支那样控制依赖。 C++11: the difference between memory_order_relaxed and memory_order_consume and 有更多细节。
例如add x2, x2, x3
指令在其两个输入寄存器都准备好之前无法执行,并且在地址准备好之前 ldr w1, [x2]
也不能执行加载,因此如果 x2
来自另一个加载,它会自动在此之前订购。 (假设 CPU 硬件设计为不违反因果关系,例如通过进行价值预测或 DEC Alpha 在极少数情况下违反因果关系所做的任何事情)。但是 cbz w1, reg_was_zero
是可以预测的,所以让 reg_was_zero: ldr w3, [x4]
等待产生 w1 的负载是不够的。 (这是 AArch64 asm,BTW,一个保证依赖排序的弱排序 ISA。)
||
或left && right
的短路评估在逻辑上与if(left) right
相同,因此分支预测+推测执行可以即使左侧尚未执行,也应 运行 右侧。 没有数据依赖,只有控制依赖。
显然逗号 left, right
不会在两边之间创建任何连接,它基本上是一种将 left; right;
塞进单个表达式的方法。
当然,如果您在左右两侧使用相同的变量,数据依赖性可能会以这种方式存在,但它不是由操作员创建的。
根据C++标准:
An evaluation A carries a dependency to an evaluation B if - the value of A is used as an operand of B, unless:
— B is an invocation of any specialization of std::kill_dependency (29.3), or
— A is the left operand of a built-in logical AND (&&, see 5.14) or logical OR (||, see 5.15) operator, or
— A is the left operand of a conditional (?:, see 5.16) operator, or
— A is the left operand of the built-in comma (,) operator (5.18); (...)
我能理解为什么在关系之前排序的依赖会在 kill_dependency 调用时停止,但为什么逻辑与、或、逗号等运算符也会破坏依赖链?
这是否意味着下面的代码有未定义的行为?
//thread1
int y = 2
atomicVal.store(true);
//thread2
auto x = atomicVal.load(std::memory_order_consume);
cout << x && y;
memory_order_consume
试图公开一个 asm 级别的 CPU 特性以供在 C++ 中使用。 (它是 kill_dependency
噪音)。理解 CPU 行为是理解旨在公开它的 C++ 设计的关键。
都是关于数据的依赖,而不是像条件分支那样控制依赖。 C++11: the difference between memory_order_relaxed and memory_order_consume and
例如add x2, x2, x3
指令在其两个输入寄存器都准备好之前无法执行,并且在地址准备好之前 ldr w1, [x2]
也不能执行加载,因此如果 x2
来自另一个加载,它会自动在此之前订购。 (假设 CPU 硬件设计为不违反因果关系,例如通过进行价值预测或 DEC Alpha 在极少数情况下违反因果关系所做的任何事情)。但是 cbz w1, reg_was_zero
是可以预测的,所以让 reg_was_zero: ldr w3, [x4]
等待产生 w1 的负载是不够的。 (这是 AArch64 asm,BTW,一个保证依赖排序的弱排序 ISA。)
||
或left && right
的短路评估在逻辑上与if(left) right
相同,因此分支预测+推测执行可以即使左侧尚未执行,也应 运行 右侧。 没有数据依赖,只有控制依赖。
显然逗号 left, right
不会在两边之间创建任何连接,它基本上是一种将 left; right;
塞进单个表达式的方法。
当然,如果您在左右两侧使用相同的变量,数据依赖性可能会以这种方式存在,但它不是由操作员创建的。