web-dev-qa-db-ja.com

memory_order_seq_cstとmemory_order_acq_relはどのように異なりますか?

ストアはリリース操作であり、ロードは両方の取得操作です。 memory_order_seq_cstは、すべての操作に追加の全順序付けを課すことを意図していることは知っていますが、すべてのmemory_order_seq_cstmemory_order_acq_relに置き換えられた場合に当てはまらない例を作成できていません。

何かが足りないのですか、それとも違いは単なるドキュメントの効果です。つまり、よりリラックスしたモデルで遊ぶつもりがない場合はmemory_order_seq_cstを使用し、リラックスしたモデルを制約する場合はmemory_order_acq_relを使用する必要がありますか?

34
AProgrammer

http://en.cppreference.com/w/cpp/atomic/memory_order 良い例があります 下部memory_order_seq_cstでのみ機能します。基本的に、memory_order_acq_relはアトミック変数に関連する読み取りと書き込みの順序を提供し、memory_order_seq_cstはグローバルに読み取りと書き込みの順序を提供します。つまり、順次一貫性のある操作は、すべてのスレッドで同じ順序で表示されます。

例はこれに要約されます:

bool x= false;
bool y= false;
int z= 0;

a() { x= true; }
b() { y= true; }
c() { while (!x); if (y) z++; }
d() { while (!y); if (x) z++; }

// kick off a, b, c, d, join all threads
assert(z!=0);

zの操作は、1つではなく2つのアトミック変数によって保護されているため、acquire-releaseセマンティクスを使用して、zが常にインクリメントされるようにすることはできません。

33
MSN

アトミックがバリアにマップされ、実際のマシンモデルにストアバッファが含まれているx86のようなISAでは、次のようになります。

  • seq_cstストアはストアバッファをフラッシュする必要があるため、このスレッドの後の読み取りは、ストアがグローバルに表示されるまで遅延されます。
  • acq_relnotストアバッファをフラッシュします。通常のx86のロードとストアには、基本的にacqとrelのセマンティクスがあります。 (seq_cstに加えて、ストア転送を備えたストアバッファー。)

    ただし、x86 asm lockプレフィックスは完全なメモリバリアであるため、x86アトミックRMW操作は常にseq_cstにプロモートされます。他のISAは、asmでrelaxedまたはacq_relRMWを実行できます。

https://preshing.com/20120515/memory-reordering-caught-in-the-act は、seq_cstストアとaの違いの有益な例です。プレーンリリースストア。(実際には、x86asmのプレーンmovに対してmfence + movです。実際にはxchgほとんどのx86CPUでseq_cstストアを実行するためのより効率的な方法ですが、GCCはmov + mfenceを使用します)


おもしろい事実:AArch64のSTLRリリースストア命令は実際にはsequential-releaseです。ハードウェアでは、relaxedまたはseq_cstを使用したロード/ストアと、フルバリア命令があります。

理論的には、STLRはストアバッファのドレインのみを必要とします次のLDARの前、他の操作の前ではありません。つまり、次のseq_cstロードの前です。実際のAArch64HWがこの方法で実装するのか、それともSTLRをコミットする前にストアバッファをドレインするだけなのかはわかりません。 (いずれの場合も、以前のすべてのストアはSTLRの前にコミットする必要がありますが、必ずしも後のプレーンロードの前にコミットする必要はありません。)

したがって、LDAR/STLRを使用してrelまたはacq_relをseq_cstに強化するのに費用がかかる必要はありません。

他の一部のISA(PowerPCなど)には、バリアの選択肢が多く、mo_relよりもmo_acq_relまたはmo_seq_cstまで安価に強化できますが、seq_cstはそれほど安くはありません。 AArch64として; seq-cstストアには完全なバリアが必要です。

3
Peter Cordes

memory_order の定義と例を引き続き使用します。ただし、me​​mory_order_seq_cstをstoreではmemory_order_releaseに、loadではmemory_order_acquireに置き換えてください。

リリース-取得順序は、発生したすべてを保証します-1つのスレッドのstoreが、ロードを行ったスレッドで目に見える副作用になる前に。しかし、この例では、thread0とthread1の両方でstoreの前には何も起こりません。

x.store(true, std::memory_order_release); // thread0

y.store(true, std::memory_order_release); // thread1

さらに、memory_order_seq_cstがないと、thread2とthread3の順番は保証されません。あなたはそれらが次のようになると想像することができます:

if (y.load(std::memory_order_acquire)) { ++z; } // thread2, load y first
while (!x.load(std::memory_order_acquire)); // and then, load x

if (x.load(std::memory_order_acquire)) { ++z; } // thread3, load x first
while (!y.load(std::memory_order_acquire)); // and then, load y

したがって、thread2とthread3がthread0とthread1の前に実行された場合、つまりxとyの両方がfalseのままであるため、++ zに触れることはなく、zは0のままであり、アサートが発生します。

ただし、me​​mory_order_seq_cstが画像に入ると、そのようにタグ付けされたすべてのアトミック操作の単一の合計変更順序が確立されます。したがって、thread2では、x.load、次にy.loadです。 thread3では、y.load、次にx.loadが確実です。

0
jun.wu