cppreferenceでの緩和された順序付けの説明に誤りはありますか?


13

cppreference.comドキュメントstd::memory_orderには、緩和された順序付けの例があります。

ゆるやかな注文

タグ付けさmemory_order_relaxedれたアトミック操作は同期操作ではありません。同時メモリアクセスに順序を課すことはありません。原子性と変更順序の一貫性のみが保証されます。

たとえば、xとyが最初はゼロの場合、

// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D

は、r1 == r2 == 42を生成することを許可されています。なぜなら、Aはスレッド1内でBの前にシーケンスされ、Cはスレッド2内でDの前にシーケンスされるからです。 xの変更順序でCの前に表示されます。yに対するDの副作用はスレッド1の負荷Aに見え、xに対するBの副作用はスレッド2の負荷Cに見える可能性があります。特に、これはDがCの前に完了する場合に発生する可能性があります。スレッド2、コンパイラの並べ替えまたは実行時のいずれかが原因。

「Cはスレッド2内でDの前にシーケンスされます」と書かれています。

評価順にあるsequenced-beforeの定義によれば、AがBの前にシーケンス化されている場合、Aの評価はBの評価が始まる前に完了します。Cはスレッド2内でDの前に順序付けられているため、Dが始まる前にCを完了する必要があるため、スナップショットの最後の文の条件部分は決して満たされません。


特にC ++ 11についての質問ですか?
curiousguy

いいえ、それはc ++ 14、17にも適用されます。コンパイラとCPUの両方がCをDで並べ替える可能性があることを知っていますが、並べ替えが発生すると、Dが始まる前にCを完了できません。したがって、「Aはスレッド1内でBの前にシーケンスされ、Cはスレッド2内でDの前にシーケンスされる」という文の誤用があると思います。「コードでは、Aはスレッド1内にBの前に配置され、Cはスレッド2内にDの前に配置されます」と言う方がより正確です。この質問の目的は、この考えを確認することです
abigaile

「並べ替え」に関しては何も定義されていません。
curiousguy

回答:


13

cppreferenceは正しいと思います。これは、「as-if」ルール[intro.execution] / 1に要約できます。コンパイラは、コードで記述されたプログラムの観察可能な動作を再現するためにのみバインドされています。Aは、配列決定の前に、関係のみこれらの評価が実行されるスレッドの観点から評価との間に確立されている[intro.execution] / 15。つまり、順番に並べられた2つの評価がスレッドのどこかにある場合、そのスレッドで実際に実行されているコードは、最初の評価が実際に行ったことが2番目の評価に影響を与えたかのように動作する必要があります。例えば

int x = 0;
x = 42;
std::cout << x;

ただし、42を出力する必要があります。ただし、コンパイラは、オブジェクトxから値を読み込んで出力する前に、値42をオブジェクトに格納する必要はありません。に保存される最後の値xが42だったことを覚えておいて、値42を実際に保存する前に値42を直接出力するだけxです。実際、xがローカル変数である場合、その変数が最後に割り当てられた値を追跡するだけで、オブジェクトを作成したり、実際に値42を格納したりすることはできません。スレッドが違いを知る方法はありません。動作が常にあることを行っているかのように、変数があったとかのように値42を実際にオブジェクトに格納されていましたx 前にそのオブジェクトから読み込まれます。ただし、これは、生成されたマシンコードが実際にどこにでも何かを保存およびロードする必要があることを意味するものではありません。必要なことは、生成されたマシンコードの観察可能な動作が、これらすべてのことが実際に発生した場合の動作と区別がつかないことです。

見てみると

r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D

はい、CはDの前にシーケンスされます。しかし、このスレッドから単独で見た場合、CがDの結果に影響を与えることは何もありません。DがCの結果を変更することはありません。別のスレッドで発生した何かの間接的な結果として。ただし、を指定するとstd::memory_order_relaxed明示的に述べたロードとストアが別のスレッドによって監視される順序は関係ありません。他のスレッドはロードを監視して特定の順序で格納できないため、CとDに一貫した方法で相互に影響を与えるために別のスレッドが実行できることはありません。したがって、ロードとストアが実際に実行される順序は重要ではありません。したがって、コンパイラはそれらを自由に並べ替えることができます。そして、その例の下の説明で述べたように、DからのストアがCからのロードの前に実行されると、r1 == r2 == 42が実際に発生します…


したがって、基本的に、標準は次のように述べています。 CはDの前に発生する必要があると規定されていますが、コンパイラはCまたはDが次に発生したかどうかを証明できないと信じており、as-ifルールにより、とにかくそれらを並べ替えますか?
1

4
@Fureeishいいえ。Cは、Dの前に、発生するスレッドが認識できる限り発生する必要があります。別のコンテキストからの観察は、その見方と一致しない場合があります。
Deduplicator

5
@curiousguyこの主張は、他の以前のC ++伝道と似ているようです。
オービットでの軽量レース

1
@curiousguy Michaelは、標準の関連する章へのリンクとともに、それについての長い説明を投稿しています。
オービットでの軽量レース

2
@curiousguy標準で、脚注の中で「as-ifルール」という規定の1つにラベルを付けてます。「この規定は、「as-if」ルールと呼ばれることもあります」intro.execution
Caleth

1

アクションが他の2つのアクションシーケンスに対して相対的に順序付けられ、それらのシーケンス内のアクションの相対的な順序付けを暗示することなく、時々可能です。

たとえば、次の3つのイベントがあるとします。

  • ストア1からp1
  • tempにp2をロードする
  • ストア2からp3

また、p2の読み取りは、p1の書き込み後かつp3の書き込み前に個別に順序付けられますが、p1とp3の両方が関与する特定の順序はありません。p2で何が行われるかに応じて、コンパイラーがp1をp3より先に延期し、それでも必要なセマンティクスをp2で実現することは非現実的です。ただし、上記のコードがより大きなシーケンスの一部であることをコンパイラが知っていたとします。

  • 1をp2にストア[p2のロード前にシーケンス]
  • [上記を実行]
  • 3をp1に格納します[p1への他のストアの後にシーケンス]

その場合、上記のコードの後に​​ストアをp1に並べ替え、次のストアと統合できると判断できるため、最初にp1を書き込まずにp3を書き込むコードが生成されます。

  • tempを1に設定
  • 温度をp2に保存
  • ストア2からp3
  • ストア3からp1

データの依存関係によりシーケンス関係の特定の部分が推移的に動作するように見えるかもしれませんが、コンパイラーは明らかなデータの依存関係が存在しない状況を特定し、したがって予期される推移的な効果をもたらさない場合があります。


1

2つのステートメントがある場合、コンパイラーはコードを順番に生成するため、最初のステートメントのコードは2番目のステートメントの前に配置されます。ただし、CPUには内部的にパイプラインがあり、アセンブリ操作を並行して実行できます。ステートメントCはロード命令です。メモリがフェッチされている間、パイプラインは次のいくつかの命令を処理し、それらがロード命令に依存していない場合、Cが完了する前に実行される可能性があります(たとえば、Dのデータがキャッシュにあり、Cがメインメモリにある)。

ユーザーが2つのステートメントを順番に実行する必要がある場合は、より厳密なメモリ順序付け操作を使用できます。一般的に、プログラムが論理的に正しい限り、ユーザーは気にしません。


-9

あなたが考えるものは何でも等しく有効です。標準は、何が順次実行されるか、何が実行されないか、そしてどのように混同されるかについては述べていません

複数のPhDに値する作業である、その混乱の上に一貫したセマンティクスを作成するのは、あなたとすべてのプログラマー次第です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.