]属性是什么意思?
有人能用一种凡人能理解的语言来解释吗?
[[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<<*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上的fence。
在第(2)行中, print
的定义是不透明的(假设它没有内联),所以编译器必须发出一个fence来确保print
*p
返回正确的值。
在第(3)行上,编译器可以假设,尽pipeprint2
也是不透明的,那么从参数到解除引用的值的依赖性被保存在指令stream中,并且POWER上不需要栅栏。 显然, print2
的定义必须实际上保留这个依赖关系,所以这个属性也会影响print2
的生成代码。
总之,如果有carry_dependency属性的话,我认为如果实际的参数真的来自另一个线程并且带有一个依赖关系的话,那么为一个函数生成的代码应该被优化。 类似的返回值。 如果这种假设不成立,可能会缺乏性能(例如在单线程中)。 但是[[carry_dependency]]的缺失可能会导致相反情况下的糟糕performance…没有其他效果,但性能改变应该发生。
例如,指针取消引用操作取决于以前如何获得指针,并且如果指针p的值来自另一个线程(通过“消费”操作),则先前由另一个线程分配给* p的值被考虑并可见。 可能有另一个指针q等于p(q == p),但由于它的值不是来自其他线程,所以* q的值可能与* p不同。 实际上* q可能会引发一种“未定义的行为”(因为访问内存位置与另一个进行赋值的线程不协调)。
真的,在某些工程案例中,似乎在内存(和思维)的function上有一些大的错误….> 🙂