問題タブ [false-sharing]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
c++ - 2 つのスレッドが同じ物理コアを共有している場合、偽共有を使用した揮発性インクリメントの実行は、デバッグよりもリリースの方が遅くなります。
偽共有のパフォーマンスへの影響をテストしようとしています。テストコードは以下のとおりです。
その結果、おおむね次のようになりました。
私のCPUはintel core i7-9750h
です。「core0」と「core1」は物理コアであり、「core2」と「core3」なども同様です。コンパイラは MSVC 14.24 を使用しました。
記録された時間は、多数のバックグラウンド タスクがあったため、複数回の実行での最高スコアのおおよその値です。結果は明確にグループに分けられ、0.1 秒~0.3 秒の誤差はグループ分けに影響を与えなかったので、これは十分公平だったと思います。
Test2 の説明は非常に簡単でした。x
とは異なるキャッシュ ラインにあるためy
、2 つの物理コアで実行するとパフォーマンスが 2 倍向上します (シングル コアで 2 つのスレッドを実行する場合のコンテキスト スイッチのコストはここでは無視できます)。物理コア、coffee-lake のスループットによって制限され (Ryzen の方がわずかに優れていると信じています)、一時的なマルチスレッドよりも効率的です。ここでは 64bit モードの方が効率が良いようです。
しかし、test1 の結果はわかりにくいです。まず、デバッグ モードでは、0-2、0-3、および 0-5 は 0-0 よりも遅く、これは理にかなっています。これは、特定のデータが L1 から L3 へ、L3 から L1 へと繰り返し移動されるためであると説明しました。これは、キャッシュが 2 つのコア間で一貫性を保つ必要があるためです。一方、単一のコアで実行している場合、キャッシュは常に L1 にとどまります。しかし、この理論は、0-1 ペアが常に最も遅いという事実と矛盾します。技術的には、2 つのスレッドは同じ L1 キャッシュを共有する必要があります。0-1 は 0-0 の 2 倍の速さで実行する必要があります。
次に、リリース モードでは、0-2、0-3、および 0-5 が 0-0 よりも高速であり、上記の理論が反証されました。
最後に、0-1 は64 ビット モードと 32 ビット モードの両方でrelease
より遅く実行されます。debug
それが一番理解できない。生成されたアセンブリ コードを読みましたが、何も役に立ちませんでした。
x86 - False Sharing を調べると、兄弟スレッドで実行した場合に、独立したスレッドで実行した場合よりも L1d キャッシュ ミスが多いのはなぜですか
(過去に多少関連する質問がいくつかあったことは知っていますが、L1d キャッシュ ミスとハイパースレッディング/SMT に関する質問は見つかりませんでした。)
False Sharing、MESI/MOESI Cache Coherence Protocols などの非常に興味深いものについて数日間読んだ後、C で小さな「ベンチマーク」を作成して (以下を参照)、False Sharing の動作をテストすることにしました。
私は基本的に 8 つの double の配列を持っているので、1 つのキャッシュ ラインと隣接する配列位置をインクリメントする 2 つのスレッドに収まります。
この時点で、私は Ryzen 5 3600 を使用していることを述べる必要があります。そのトポロジーはここで見ることができます。
2 つのスレッドを作成し、それらを 2 つの異なる論理コアに固定し、それぞれが独自の配列位置にアクセスして更新します。つまり、スレッド A は array[2] を更新し、スレッド B は array[3] を更新します。
同じコアに属するハードウェア スレッド#0と#6を使用してコードを実行すると、(トポロジ ダイアグラムに示すように) L1d キャッシュを共有すると、実行時間は約 5 秒になります。
共通のキャッシュを持たないスレッド#0と#11を使用すると、完了するまでに ~ 9.5 秒かかります。この場合、「キャッシュ ライン ピンポン」が進行しているため、この時間差が予想されます。
ただし、スレッド#0および#11を使用している場合、L1d キャッシュ ミスはスレッド#0および#6で実行する場合よりも少なくなります。
私の推測では、共通キャッシュを持たないスレッド#0と#11を使用している場合、一方のスレッドが共有キャッシュ ラインの内容を更新すると、MESI/MOESI プロトコルに従って、もう一方のコアのキャッシュ ラインが無効になります。そのため、ピンポンが発生していても、(スレッド#0および#6で実行している場合と比較して) キャッシュ ミスはそれほど多くなく、一連の無効化とキャッシュ ライン ブロックがコア間で転送されるだけです。
では、共通の L1d キャッシュを持つスレッド #0 と #6 を使用すると、キャッシュ ミスが増えるのはなぜでしょうか?
(スレッド#0と#6にも共通の L2 キャッシュがありますが、ここでは重要ではないと思います。キャッシュ ラインが無効になると、メイン メモリ (MESI) または別のコアのいずれかからフェッチする必要があるためです。キャッシュ (MOESI) であるため、L2 が必要なデータを保持することさえ不可能であるように思われますが、それを要求することもできません) .
もちろん、1 つのスレッドが L1d キャッシュ ラインに書き込むと、キャッシュ ラインは「ダーティ」になりますが、なぜそれが問題になるのでしょうか? 同じ物理コアに存在する他のスレッドは、新しい「ダーティ」値を問題なく読み取れるはずではありませんか?
TLDR : False Sharing をテストすると、2 つの兄弟スレッド (同じ物理コアに属するスレッド) を使用すると、2 つの異なる物理コアに属するスレッドを使用する場合よりも、L1d キャッシュ ミスが約3 倍多くなります。(2.34% 対 0.75% のミス率、3 億 9600 万対 1 億 1800 万のミスの絶対数)。なぜそれが起こっているのですか?
(L1d キャッシュ ミスなどのすべての統計は、Linux の perf ツールを使用して測定されます。)
また、マイナーな二次的な質問ですが、兄弟スレッドが 6 桁離れた ID でペアになっているのはなぜですか? つまり、スレッド 0 の兄弟はスレッド 6 です。スレッド i の兄弟はスレッド i+6 です。それは何らかの形で役立ちますか?Intel と AMD の両方の CPU でこれに気付きました。
私はコンピュータ アーキテクチャに非常に興味があり、まだ学習中なので、上記のいくつかは間違っている可能性があります。申し訳ありません。
だから、これは私のコードです。2 つのスレッドを作成し、それらを特定の論理コアにバインドしてから、隣接するキャッシュ ラインの場所にアクセスするだけです。
私はGCC 10.2.0 Compilingを次のように使用していますgcc -pthread p.c -o p
次に、perf record ./p --cpu=0,6
スレッド 0,6 と 0,11 をそれぞれ使用する場合、 --cpu=0,11 で実行するか、同じことを実行します。
perf stat -d ./p --cpu=0,6
他の場合は --cpu=0,11 で実行中または同じ
スレッド0と6で実行:
スレッド0と11で実行: