42

std::atomic<T>T がプリミティブ型の場合:

操作、操作、および操作に盲目的に使用する場合std::memory_order_acq_rel(これらの関数のデフォルトのメモリ順序をリセットするのと同じことを意味します)fetch_xxxstd::memory_order_acquireloadstd::memory_order_releasestore

  • std::memory_order_seq_cst結果は、宣言された操作のいずれかに使用した場合 (デフォルトとして使用されています)と同じになりますか?
  • std::memory_order_seq_cst結果が同じだった場合、この使用法は効率の点で使用法とは何か違いますか?
4

2 に答える 2

85

アトミック操作の C++11 メモリ順序付けパラメーターは、順序付けに関する制約を指定します。でストアを実行し、std::memory_order_release別のスレッドからのロードが で値を読み取る場合std::memory_order_acquire、2 番目のスレッドからの後続の読み取り操作では、最初のスレッドによって任意のメモリ位置に格納された、ストア リリースの前、またはそれ以降の値が表示されます。これらのメモリ ロケーションのいずれかに保存します

ストアと後続のロードの両方が である場合、std::memory_order_seq_cstこれら 2 つのスレッド間の関係は同じです。違いを確認するには、より多くのスレッドが必要です。

たとえば、std::atomic<int>変数xy、どちらも最初は 0 です。

スレッド 1:

x.store(1,std::memory_order_release);

スレッド 2:

y.store(1,std::memory_order_release);

スレッド 3:

int a=x.load(std::memory_order_acquire); // x before y
int b=y.load(std::memory_order_acquire); 

スレッド 4:

int c=y.load(std::memory_order_acquire); // y before x
int d=x.load(std::memory_order_acquire);

x書かれているように、とのストアの間に関係はありません。そのため、スレッド 3 とスレッド 4 で 、およびがy表示される可能性は十分にあります。a==1b==0c==1d==0

すべてのメモリ順序が に変更された場合、ストアとstd::memory_order_seq_cstの間の順序が強制されます。したがって、スレッド 3 がandを参照した場合、それはストア to がストア to の前にある必要があることを意味し、スレッド 4 が を参照した場合、つまりストア toが完了したことを意味し、ストア toも完了している必要があるため、必要があります。xya==1b==0xyc==1yxd==1

実際には、std::memory_order_seq_cstどこでも使用すると、コンパイラとプロセッサのアーキテクチャに応じて、ロードまたはストア、またはその両方に追加のオーバーヘッドが追加されます。たとえば、x86 プロセッサの一般的な手法は、必要な順序の保証を提供するためにストアのXCHG命令ではなくMOV命令を使用することですが、プレーンの場合は十分です。より緩和されたメモリ アーキテクチャを備えたシステムでは、単純なロードとストアの保証が少ないため、オーバーヘッドが大きくなる可能性があります。std::memory_order_seq_cststd::memory_order_releaseMOV

メモリの順序付けは難しいです。私は自分の著書の中で、ほぼ 1 つの章全体をそれに当てはめました。

于 2013-02-13T22:29:40.070 に答える
12

メモリオーダリングは非常に注意が必要な場合があり、間違った場合の影響は非常に微妙なことがよくあります。

すべてのメモリオーダリングの重要なポイントは、何が起こるかではなく、「起こったこと」を保証することです。たとえば、いくつかの変数(たとえば)に何かを格納すると、別のプロセッサがxの値を認識する前に11と見なすx = 7; y = 11;ことができる場合があります。設定と設定の間にメモリオーダリング操作を使用することにより、使用しているプロセッサは、に何かを格納し続ける前に、メモリに書き込まれたことを保証します。y7xyx = 7;y

ほとんどの場合、値が最終的に更新される限り、書き込みが発生する順序はそれほど重要ではありません。しかし、たとえば、整数の循環バッファがあり、次のようなことを行う場合:

buffer[index] = 32;
index = (index + 1)  % buffersize; 

そして、他のスレッドがindex新しい値が書き込まれたことを判別するために使用しているので32、最初に書き込んでから、後でindex更新する必要があります。そうしないと、他のスレッドがoldデータを取得する可能性があります。

セマフォやミューテックスなどを機能させる場合にも同じことが当てはまります。これが、メモリバリアタイプにリリースおよび取得という用語が使用される理由です。

現在、これcstは最も厳密な順序付けルールです。これにより、プロセッサがさらに操作を続行する前に、書き込んだデータの読み取りと書き込みの両方がメモリに出力されます。これは、特定の取得または解放の障壁を実行するよりも遅くなります。これにより、プロセッサは、ストアまたはロードだけではなく、ストアとロードが完了したことを確認するように強制されます。

それはどのくらいの違いがありますか?これは、システムアーキテクチャが何であるかに大きく依存します。一部のシステムでは、キャッシュを[部分的に]フラッシュする必要があり、あるコアから別のコアに割り込みを送信して、「続行する前にこのキャッシュフラッシュ作業を実行してください」と言います。これには数百サイクルかかる場合があります。他のプロセッサでは、通常のメモリ書き込みを行うよりもわずかな割合で遅くなります。X86は、これを高速に実行するのに非常に優れています。一部のタイプの組み込みプロセッサ(たとえば、ARMの一部のモデル-わからない?)は、すべてが機能することを保証するために、プロセッサでもう少し作業が必要です。

于 2013-02-13T20:07:07.403 に答える