[[carries_dependency]]属性是什么意思

What does the [[carries_dependency]] attribute mean?

本文关键字:是什么 意思 属性 dependency carries      更新时间:2023-10-16

有人能用普通人能理解的语言解释吗?

[[carries_dependency]]用于允许在函数调用之间传递依赖关系。这可能允许编译器在与std::memory_order_consume一起使用时生成更好的代码,用于在具有弱有序体系结构(如IBM的POWER体系结构)的平台上的线程之间传输值。

特别地,如果用memory_order_consume读取的值被传递给函数,那么在没有[[carries_dependency]]的情况下,编译器可能不得不发出内存围栏指令,以保证维持适当的内存排序语义。如果参数是用[[carries_dependency]]注释的,那么编译器可以假设函数体将正确地携带依赖项,并且可能不再需要这个围栏。

类似地,如果函数返回用memory_order_consume加载的值,或者从这样的值派生的值,那么在没有[[carries_dependency]]的情况下,编译器可能需要插入fence指令,以确保支持适当的内存排序语义。有了[[carries_dependency]]注释,这个栅栏可能不再是必要的,因为调用者现在负责维护依赖树。

例如

void print(int * val)
{
    std::cout<<*val<<std::endl;
}
void print2(int * [[carries_dependency]] val)
{
    std::cout<<*val<<std::endl;
}
std::atomic<int*> p;
int* local=p.load(std::memory_order_consume);
if(local)
    std::cout<<*local<<std::endl; // 1
if(local)
    print(local); // 2
if(local)
    print2(local); // 3

在第(1)行中,依赖关系是显式的,因此编译器知道local被取消引用,并且它必须确保保留依赖链,以避免POWER上的围栏。

在第(2)行中,print的定义是不透明的(假设它没有内联),因此编译器必须发出一个围栏,以确保在print中读取*p返回正确的值。

在第(3)行,编译器可以假设,尽管print2也是不透明的,但从参数到去引用值的依赖关系保留在指令流中,并且POWER上不需要围栏。显然,print2的定义实际上必须保留这种依赖关系,因此属性也会影响为print2生成的代码。

简而言之,我认为,如果有carries_dependency属性,那么为函数生成的代码应该针对某个情况进行优化,因为实际的参数确实来自另一个线程并带有依赖项。返回值也是如此。若那个种假设不成立(例如在单线程程序中),可能会出现性能不足的情况。但在相反的情况下,缺少[[carries_dependency]]可能会导致糟糕的性能。。。除了性能改变之外,没有其他影响。

例如,指针取消引用操作取决于指针之前是如何获得的,如果指针p的值来自另一个线程(通过"consume"操作),则会考虑到该另一线程先前分配给*p的值,并且该值是可见的。可能存在另一个等于p(q==p)的指针q,但由于其值不是来自该另一个线程,因此可以看出*q的值与*p不同。实际上,*q可能会引发一种"未定义的行为"(因为访问内存位置和进行赋值的另一个线程不协调)。

实际上,在某些工程案例中,内存(和大脑)的功能似乎存在一些大缺陷……>:-)