そこで私は、来たる C++0x 標準の一部であるメモリ モデルについて読んでいました。ただし、コンパイラーが許可されていること、特に投機的なロードとストアに関するいくつかの制限については、少し混乱しています。
まず、関連するもののいくつか:
C++0x のスレッドとメモリ モデルに関する Hans Boehm のページ
Boehm と Adve、「C++ 同時実行メモリ モデルの基礎」
Sutter、「Prism: Microsoft ネイティブ コード プラットフォーム向けの原理ベースのシーケンシャル メモリ モデル」、N2197
Boehm、「同時実行メモリ モデル コンパイラの結果」、N2338
現在、基本的な考え方は本質的に「データ競合のないプログラムの順次整合性」です。これは、プログラミングの容易さと、コンパイラとハードウェアの機会を最適化できるようにすることとの間の適切な妥協点のようです。データ競合は、異なるスレッドによる同じメモリ ロケーションへの 2 つのアクセスが順序付けられておらず、そのうちの少なくとも 1 つがメモリ ロケーションに格納され、そのうちの少なくとも 1 つが同期アクションではない場合に発生するように定義されています。これは、共有データへのすべての読み取り/書き込みアクセスが、ミューテックスやアトミック変数の操作など、何らかの同期メカニズムを介して行われる必要があることを意味します (まあ、専門家のみが緩和されたメモリ順序でアトミック変数を操作することは可能ですが、デフォルトではシーケンシャルな一貫性のため)。
これに照らして、通常の共有変数に対するスプリアスまたは投機的なロード/ストアに関する制限について混乱しています。たとえば、N2338 には次の例があります。
switch (y) {
case 0: x = 17; w = 1; break;
case 1: x = 17; w = 3; break;
case 2: w = 9; break;
case 3: x = 17; w = 1; break;
case 4: x = 17; w = 3; break;
case 5: x = 17; w = 9; break;
default: x = 17; w = 42; break;
}
コンパイラが変換することを許可されていない
tmp = x; x = 17;
switch (y) {
case 0: w = 1; break;
case 1: w = 3; break;
case 2: x = tmp; w = 9; break;
case 3: w = 1; break;
case 4: w = 3; break;
case 5: w = 9; break;
default: w = 42; break;
}
y == 2 の場合、x への偽の書き込みがあり、別のスレッドが x を同時に更新している場合に問題になる可能性があるためです。しかし、なぜこれが問題なのですか?これはデータ競合であり、とにかく禁止されています。この場合、コンパイラは x に 2 回書き込むことで状況を悪化させますが、1 回の書き込みでもデータ競合には十分ですよね? つまり、適切な C++0x プログラムは x へのアクセスを同期する必要があります。その場合、データ競合はなくなり、スプリアスストアも問題になりませんか?
N2197 の例 3.1.3 と他のいくつかの例についても同様に混乱していますが、上記の問題の説明でそれも説明できるかもしれません。
編集:答え:
投機的ストアが問題となる理由は、上記の switch ステートメントの例では、y != 2 の場合に限り x を保護するロックを条件付きで獲得することをプログラマーが選択した可能性があるためです。元のコードであるため、変換は禁止されています。同じ議論が N2197 の例 3.1.3 にも当てはまります。