38

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

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

クリティカル セクション、ミューテックス、イベントなどについては既に認識しており、使用したことがあります。どの力を認識していないか、キャッシュの一貫性を強制するために使用できるx86組み込み関数があるかどうか、私は主に疑問に思っています。

4

9 に答える 9

36

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

x86 には、多くのキャッシュ コヒーレンシ命令はありません。のようなプリフェッチ命令がありますがprefetchnta、メモリ順序付けのセマンティクスには影響しません。以前は、L2 を汚染することなく L1 キャッシュに値をもたらすことで実装されていましたが、大規模な共有包括的L3 キャッシュを備えた最新の Intel 設計では状況がより複雑になっています。

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


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

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

于 2009-02-17T21:58:05.247 に答える
25

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

于 2009-02-17T22:06:09.550 に答える
18

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

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

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


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

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

于 2009-02-17T21:54:47.733 に答える
7

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

于 2009-02-17T21:51:30.707 に答える
6

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

物事を移植可能にしたい場合は、はるかに困難な道を歩むことになります.

于 2009-02-17T21:54:52.290 に答える
3

最新のメモリ アーキテクチャを説明する一連の記事がここにあります。これには、Intel Core2 キャッシュやその他の多くの最新のアーキテクチャに関するトピックが含まれます。

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

于 2009-02-18T13:30:52.510 に答える
2

volatile以下は、スレッド化されたプログラムでの使用に関する参考になる良い記事です。

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

于 2009-09-22T02:18:17.910 に答える