有人能用一种凡人理解的语言解释它吗?
答案 0 :(得分:53)
[[carries_dependency]]
用于允许跨函数调用传递依赖项。这可能允许编译器在与std::memory_order_consume
一起使用时生成更好的代码,以便在具有弱有序体系结构的平台上的线程之间传输值,例如IBM的POWER体系结构。
特别是,如果使用memory_order_consume
读取的值传入函数,然后没有[[carries_dependency]]
,则编译器可能必须发出内存栅栏指令以保证适当的内存排序语义坚持。如果参数使用[[carries_dependency]]
注释,则编译器可以假定函数体将正确地携带依赖关系,并且可能不再需要此栅栏。
类似地,如果函数返回一个加载了memory_order_consume
的值,或者从这样的值派生,那么在没有[[carries_dependency]]
的情况下,编译器可能需要插入一个fence指令来保证适当的内存排序语义得到维护。使用[[carries_dependency]]
注释,可能不再需要此栅栏,因为调用者现在负责维护依赖关系树。
e.g。
void print(int * val)
{
std::cout<<*p<<std::endl;
}
void print2(int * [[carries_dependency]] val)
{
std::cout<<*p<<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
的定义是不透明的(假设它没有内联),因此编译器必须发出一个fence,以确保在{{1}中读取*p
返回正确的值。
在第(3)行,编译器可以假设虽然print
也是不透明的,但是从参数到解除引用值的依赖关系会保留在指令流中,并且POWER上不需要任何围栅。显然,print2
的定义必须实际保留此依赖关系,因此该属性也会影响print2
生成的代码。
答案 1 :(得分:-3)
简而言之,我认为,如果有carry_dependency属性,那么函数的生成代码应针对一个案例进行优化,而实际的参数将真正来自另一个线程并带有依赖关系。类似地,对于返回值。如果该假设不正确,则可能缺乏性能(例如,在单线程程序中)。但是,[[carry_dependency]]的缺失可能导致相反情况下的糟糕表现......没有其他影响,但性能会发生变化。
例如,指针取消引用操作取决于先前如何获得指针,以及指针p的值是否来自另一个线程(通过“消耗”操作),之前由另一个线程分配给* p的值是考虑到并且可见。可能存在另一个指针q,其等于p(q == p),但是由于其值不是来自该另一个线程,所以可以看到* q的值与* p不同。实际上* q可能会引发一种“未定义的行为”(因为访问内存位置与另一个进行分配的线程协调)。
实际上,在某些工程案例中,内存(和思想)的功能似乎存在一些大错误....&gt;: - )