Lif*_*ang 4 c++ atomic lock-free carries-dependency stdatomic
根据C++标准:
\n\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
我可以理解为什么关系之前排序的依赖项会在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;\nRun Code Online (Sandbox Code Playgroud)\n
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;单个表达式的方法。
当然,如果您在左侧和右侧使用相同的变量,则可以以这种方式存在数据依赖性,但它不是由运算符创建的。
| 归档时间: |
|
| 查看次数: |
150 次 |
| 最近记录: |