c++ - [[carries_dependency]] 什么意思以及如何实现

标签 c++ c++11 memory-model stdatomic carries-dependency

我在这个 SO 中读到了 [[carries_dependency]]发布。

但我无法理解的是接受的答案中的以下句子:

"In particular, if a value read with memory_order_consume is passed in to a function, then without [[carries_dependency]], then the compiler may have to issue a memory fence instruction to guarantee that the appropriate memory ordering semantics are upheld. If the parameter is annotated with [[carries_dependency]] then the compiler can assume that the function body will correctly carry the dependency, and this fence may no longer be necessary.

Similarly, if a function returns a value loaded with memory_order_consume, or derived from such a value, then without [[carries_dependency]] the compiler may be required to insert a fence instruction to guarantee that the appropriate memory ordering semantics are upheld. With the [[carries_dependency]] annotation, this fence may no longer be necessary, as the caller is now responsible for maintaining the dependency tree."

让我们一步一步来:

"if a value read with memory_order_consume is passed in to a function, then without [[carries_dependency]], then the compiler may have to issue a memory fence instruction to guarantee that the appropriate memory ordering semantics are upheld."

因此,对于释放-消耗内存模型中的原子变量,当原子变量作为参数传递给函数时,编译器将引入栅栏硬件指令,以便它始终具有提供给的原子变量的最新和更新值函数。

下一步 -

"If the parameter is annotated with [[carries_dependency]] then the compiler can assume that the function body will correctly carry the dependency, and this fence may no longer be necessary."

这让我感到困惑 - 原子变量值已经被消耗,然后该函数携带什么依赖项?

同样-

"if a function returns a value loaded with memory_order_consume, or derived from such a value, then without [[carries_dependency]] the compiler may be required to insert a fence instruction to guarantee that the appropriate memory ordering semantics are upheld. With the [[carries_dependency]] annotation, this fence may no longer be necessary, as the caller is now responsible for maintaining the dependency tree."

从示例中不清楚它试图说明携带依赖项的要点是什么?

最佳答案

仅供引用,memory_order_consume (和 [[carries_dependency]] )基本上已被弃用,因为编译器很难有效和正确地实现 C++11 设计规则的方式。 (和/或因为 [[carries_dependency]] 和/或 kill_dependency 最终到处都需要。)见 P0371R1: Temporarily discourage memory_order_consume .

当前的编译器只处理 mo_consume作为mo_acquire (因此在需要一个的 ISA 上,在消耗负载之后放置一个屏障)。如果你想要无障碍的数据依赖排序性能,你必须通过使用 mo_relaxed 来欺骗编译器。并仔细编码以避免编译器可能在没有实际依赖的情况下创建 asm 的事情。 (例如 Linux RCU)。参见 C++11: the difference between memory_order_relaxed and memory_order_consume有关更多详细信息和链接,以及 mo_consume 的 asm 功能旨在揭露。

还有 Memory order consume usage in C11 .
了解依赖排序的概念(在 asm 中)对于理解此 C++ 功能的设计方式基本上是必不可少的。

When [an] atomic variable is being passed as a parameter to the function the compiler will introduce a fence hardware instruction ...

首先,您不会“将原子变量传递给”函数;那甚至意味着什么?如果您传递一个指针或对原子对象的引用,该函数将从它自己加载,并且该函数的源代码将使用 memory_order_consume还是不是。

相关的事情是从带有 mo_consume 的原子变量加载传递一个值。像这样:

    int tmp = shared_var.load(std::memory_order_consume);
    func(tmp);

func可以使用该 arg 作为 atomic<int> 数组的索引做一个mo_relaxed加载。对于要在 shared_var.load 之后依赖排序的负载即使没有内存屏障,func 的代码生成必须确保加载对 arg 具有 asm 数据依赖性,即使 C++ 代码执行类似 tmp -= tmp; 的操作也是如此。编译器通常只会将其视为 tmp = 0; (杀死以前的值(value))。

但是[[carries_dependency]]将使编译器在实现类似于 array[idx+tmp] 的东西时仍然引用具有数据依赖性的归零值.

the atomic variable value is already consumed and then what dependency the function is carried?

“已消费”不是一个有效的概念。整点consume而不是 acquire是因为它们对 mo_consumedata 依赖性,所以后面的加载顺序正确加载结果,让您避开障碍。如果您希望它在原始加载之后排序,那么以后的每个加载都需要这样的依赖关系;没有任何意义可以说某个值“已被消耗”。

如果由于某个函数缺少 carrys_dependency 而最终插入一个屏障来促进 consume 获取,则后面的函数将不需要另一个屏障,因为您可以说该值“已经获取”。 (尽管这不是标准术语。您会在加载后订购第一个障碍后说代码。)


了解 Linux 内核如何处理这个问题可能会很有用,因为它们使用手动滚动的原子操作和它们支持的有限编译器集。在中搜索“依赖项” https://github.com/torvalds/linux/blob/master/Documentation/memory-barriers.txt ,并注意像 if(flag) data.load() 这样的“控制依赖”之间的区别与 data[idx].load 之类的数据依赖关系.

IIRC,即使是 C++ 也不能保证 mo_consume当依赖项是条件条件时的依赖项排序,如 if(x.load(consume)) tmp=y.load(); .

请注意,如果只有 2 个可能的值,编译器有时会将数据依赖项转换为控制依赖项。这会破坏 mo_consume , 如果值来自 mo_consume 则不允许进行优化加载或 [[carries_dependency]]函数参数。这就是为什么难以实现的部分原因;这将需要教授大量关于数据依赖排序的优化过程,而不是仅仅期望用户编写不执行通常会优化掉的事情的代码。 (喜欢tmp -= tmp;)

关于c++ - [[carries_dependency]] 什么意思以及如何实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64113244/

相关文章:

c++ - 如何将 constexpr 值传递给采用 const 引用的 CUDA 设备端函数?

c++ - 在类中强制可变参数模板成员函数实例化

c++ - 以宽松的顺序修改原子值

c++ - 返回 "file already exists"错误的窗口类的 RegisterClass

c++ - wxWidgets BoxSizer 没有扩展到全宽

C++ 字符串问题——如何访问字符串元素?

python - 将字符串转换为字节会在控制台中打印出奇怪的十六进制代码

c++ - 没有空闲存储内存分配的 std::string

java - 由于内存不一致,线程能否观察到对象中的垃圾值?

C++0x : memory ordering