有人能用普通人听得懂的语言解释一下吗?
有人能用普通人听得懂的语言解释一下吗?
[[carries_dependency]]
用于允许依赖关系跨越函数调用。在具有弱序架构(如IBM的POWER架构)的平台上与 std::memory_order_consume
结合使用时,这可能会使编译器生成更好的代码。
特别地,如果使用 memory_order_consume
读取的值被传递给一个函数,那么如果没有 [[carries_dependency]]
的话,编译器可能需要发出内存栅栏指令来保证适当的内存顺序语义得到遵守。如果参数带有 [[carries_dependency]]
注释,那么编译器可以假定函数体将正确地传递依赖关系,因此这个栅栏可能不再必要。
同样地,如果一个函数返回使用 memory_order_consume
加载或从这样的值导出的值,则如果没有 [[carries_dependency]]
的话,编译器可能需要插入一个栅栏指令来保证适当的内存顺序语义得到遵守。使用 [[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
在第一行中,该依赖关系是显式的,因此编译器知道 local
被解引用,并且必须确保依赖链被保留以避免在 POWER 上造成障碍。
在第二行中,print
的定义是不透明的(假设没有内联),因此编译器必须发出障碍,以确保在 print
中读取 *p
返回正确的值。
在第三行中,编译器可以假设虽然 print2
也是不透明的,但从参数到解引用值的依赖关系在指令流中得到保留,因此在 POWER 上不需要障碍。显然,print2
的定义实际上必须保留此依赖关系,因此属性还将影响生成的 print2
代码。
[[carries_dependency]]
属性,不必调用std::kill_dependency
函数,除非你确实需要它。编译器会确保在生成的代码中不会破坏依赖关系链。 - Anthony Williams[[carries_dependency]]
,编译器就会神奇地生成更快的代码。我很想看一个不能使用[[carries_dependency]]
或者必须使用std::kill_dependency
的示例函数。 - Marc Mutz - mmutz
d?a:b
会破坏依赖性,但d->static_fun()
却不会……这毫无意义。而且它也不允许“稍微好一点的多线程代码”,避免频繁操作的屏障对某些处理器来说显著更好。"当经常读取的数据很少修改时,可以共享" Consume也适用于经常修改的数据,只要有指向它的指针,并且记录只发布一次并且只读一次,这通常是规范。 - curiousguy