誰かがそれを単なる人間が理解できる言語で説明できますか?
2 に答える
[[carries_dependency]]
依存関係を関数呼び出し間で実行できるようにするために使用されます。std::memory_order_consume
これにより、 IBMのPOWERアーキテクチャーなど、順序の弱いアーキテクチャーを備えたプラットフォーム上のスレッド間で値を転送するためにコンパイラーを使用すると、より優れたコードを生成できる可能性があります。
特に、withで読み取られた値が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
行(1)では、依存関係が明示的であるため、コンパイラーはそれlocal
が逆参照されていることを認識し、POWERのフェンスを回避するために、依存関係チェーンが保持されていることを確認する必要があります。
(2)行目では、の定義は不透明であるため(インライン化されていないと仮定)、コンパイラーは、読み込みによって正しい値が返さprint
れるようにするためにフェンスを発行する必要があります。*p
print
行(3)で、コンパイラーは、print2
不透明でもあるものの、パラメーターから逆参照された値への依存関係が命令ストリームに保持され、POWERにフェンスが必要ないと想定できます。明らかに、の定義はprint2
実際にこの依存関係を保持する必要があるため、属性はの生成されたコードにも影響を与えますprint2
。
つまり、carrys_dependency属性がある場合、関数用に生成されたコードは、実際の引数が実際に別のスレッドから取得され、依存関係を持っている場合に最適化する必要があると思います。戻り値についても同様です。その仮定が当てはまらない場合(たとえば、シングルスレッドプログラムの場合)、パフォーマンスが低下する可能性があります。ただし、[[carries_dependency]]がないと、逆の場合にパフォーマンスが低下する可能性があります...他の影響はありませんが、パフォーマンスが変化するはずです。
たとえば、ポインターの逆参照操作は、ポインターが以前に取得された方法に依存し、ポインターpの値が別のスレッドからのものである場合(「消費」操作によって)、その別のスレッドによって*pに以前に割り当てられた値が考慮されます。目に見える。p(q == p)に等しい別のポインターqが存在する可能性がありますが、その値は他のスレッドからのものではないため、*qの値は*pとは異なる場合があります。実際、* qは一種の「未定義動作」を引き起こす可能性があります(割り当てを行った別のスレッドとの調整からメモリ位置にアクセスするため)。
本当に、特定のエンジニアリングの場合、メモリ(およびマインド)の機能にいくつかの大きなバグがあるようです....> :-)