web-dev-qa-db-ja.com

マルチコアx86CPUでキャッシュコヒーレンシを強制できますか?

先週、スレッド間の通信を可能にするために、小さなスレッドクラスと一方向のメッセージパイプを作成しました(明らかに、双方向通信のために、スレッドごとに2つのパイプ)。 Athlon 64 X2ではすべて正常に機能しましたが、両方のスレッドが同じ変数を参照していて、各コアのこの変数のローカルキャッシュ値が同期していない場合、問題が発生するかどうか疑問に思いました。

volatileキーワードによって変数がメモリから強制的に更新されることはわかっていますが、マルチコアx86プロセッサで、すべてのコアのキャッシュを強制的に同期させる方法はありますか?これは私が心配する必要があるものですか、それともvolatileそして軽量ロックメカニズムの適切な使用(私は_InterlockedExchangeを使用して揮発性パイプ変数を設定していました)は「ロックフリー」を書きたいすべてのケースを処理しますマルチコアx86CPUのコード?

私はすでにクリティカルセクション、ミューテックス、イベントなどを認識して使用しています。キャッシュコヒーレンシを強制するためにどの力を使用できるかを知らないx86組み込み関数があるかどうか、ほとんど疑問に思っています。

34
Furious Coder

volatileは、コードに値の再読み取りを強制するだけで、値の読み取り元を制御することはできません。値が最近コードによって読み取られた場合は、おそらくキャッシュ内にあります。その場合、volatileは、メモリからではなく、キャッシュから値を強制的に再読み取りします。

X86には多くのキャッシュコヒーレンシ命令はありません。 prefetchnta のようなプリフェッチ命令がありますが、それはメモリ順序のセマンティクスには影響しません。以前は、L2を汚染せずに値をL1キャッシュに取り込むことで実装されていましたが、大規模な共有を含むL3キャッシュを備えた最新のIntel設計では、事態はより複雑になります。 。

x86 CPUは、 MESIプロトコル (Intelの場合はMESIF、AMDの場合はMOESI)のバリエーションを使用して、キャッシュを相互にコヒーレントに保ちます(異なるコアのプライベートL1キャッシュを含む)。キャッシュラインを書き込みたいコアは、自身のコピーを共有状態から変更状態に変更する前に、他のコアにそのコピーを無効にするように強制する必要があります。


X86のロード/ストアには 取得/解放セマンティクス が組み込まれているため、1つのスレッドでデータを生成して別のスレッドでデータを消費するためのフェンス命令(MFENCEなど)は必要ありません。逐次一貫性を得るには、MFENCE(完全なバリア)が必要です。 (この回答の以前のバージョンでは、clflushが必要であることが示唆されていましたが、これは正しくありません)。

C++のメモリモデルは順序が弱いため、 コンパイル時の並べ替え を防ぐ必要があります。 volatileは、これを行うための古くて悪い方法です。 C++ 11 std :: atomicは、ロックフリーコードを作成するためのはるかに優れた方法です。

31
SoapBox

X86プロセッサで採用されているMESIプロトコルにより、コア間のキャッシュコヒーレンスが保証されます。データがコアのキャッシュにある間にメモリにアクセスする可能性のある外部ハードウェアを扱う場合にのみ、メモリの一貫性について心配する必要があります。ただし、テキストはユーザーランドでプログラミングしていることを示唆しているため、ここではあなたのケースのようには見えません。

24
Hamilton

キャッシュの一貫性について心配する必要はありません。ハードウェアがそれを処理します。心配する必要があるのは、そのキャッシュの一貫性によるパフォーマンスの問題です。

Core#1が変数に書き込むと、他のコアのキャッシュラインの他のすべてのコピーが無効になります(ストアをコミットする前に、キャッシュラインの 排他的所有権 を取得する必要があるため)。 core#2が同じ変数を読み取ると、キャッシュで失われます(core#1がキャッシュの共有レベルまで既に書き戻している場合を除く)。

キャッシュライン全体(64バイト)をメモリから読み取る(または共有キャッシュに書き戻してからcore#2で読み取る)必要があるため、パフォーマンスコストが発生します。この場合、それは避けられません。これは望ましい動作です。


問題は、同じキャッシュラインに複数の変数がある場合、コアが同じキャッシュライン内で異なる変数を読み書きしている場合でも、プロセッサがキャッシュの同期を維持するために余分な時間を費やす可能性があることです。

これらの変数が同じキャッシュラインにないことを確認することで、そのコストを回避できます。この効果は、実際にはスレッド間で共有されていないオブジェクトの値をプロセッサに同期させるため、偽共有として知られています。

15
Ferruccio

揮発性はそれをしません。 C++では、volatileは、メモリではなくレジスタに変数を格納したり、変数を完全に削除したりするなど、コンパイラの最適化にのみ影響します。

6
dsimcha

使用しているコンパイラを指定していませんが、Windowsを使用している場合は、 この記事はこちら をご覧ください。また、利用可能なs 同期関数はこちら もご覧ください。一般に、volatileは目的の処理を実行するのに十分ではありませんが、VC 2005および2008では、非標準のセマンティクスが追加されています。読み取りと書き込みの周りに暗黙のメモリバリアを追加します。

物を持ち運びしたいのなら、あなたはあなたの前にはるかに困難な道を歩むでしょう。

6
Eclipse

あなたの質問にはいくつかのサブ質問があるので、私の知る限りそれらに答えます。

  1. 現在、C++でロックフリーの相互作用を実装する移植可能な方法はありません。 C++ 0xの提案は、アトミックライブラリを導入することでこれを解決します。
  2. Volatileは、マルチコアでアトミック性を提供することが保証されておらず、その実装はベンダー固有です。
  3. X86では、マルチスレッドコードを壊す可能性のある一部のコンパイラ最適化を防ぐために共有変数を揮発性として宣言することを除いて、特別なことをする必要はありません。 Volatileは、値をキャッシュしないようにコンパイラーに指示します。
  4. 揮発性変数を使用するx86でも機能しないアルゴリズム(Dekkerなど)がいくつかあります。
  5. スレッド間でデータへのアクセスを渡すことがプログラムの主要なパフォーマンスのボトルネックであることが確実にわかっている場合を除いて、ロックフリーソリューションには近づかないでください。値またはロックによるデータの受け渡しを使用します。
3

最新のメモリアーキテクチャを説明する一連の記事があります ここIntel Core2キャッシュ およびより多くの最新のアーキテクチャトピックを含みます。

記事は非常に読みやすく、よく説明されています。楽しい !

3
davidnr

以下は、スレッド化されたプログラムを使用したvolatileの使用に関する優れた記事です。

マルチスレッドプログラミングにはほとんど役に立たない揮発性

2
cmcginty

ハーブサッターは単純に 提案 任意の2つの変数が別々のキャッシュラインに存在する必要があるように見えました。彼は、ロックとノードポインターの間にパディングを使用して、並行キューでこれを実行します。

編集:IntelコンパイラまたはGCCを使用している場合は、 アトミックビルトイン を使用できます。これは、可能な場合はキャッシュをプリエンプションするために最善を尽くしているようです。

1
greyfade