問題タブ [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.

0 投票する
2 に答える
98 参照

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それが一番理解できない。生成されたアセンブリ コードを読みましたが、何も役に立ちませんでした。

0 投票する
0 に答える
84 参照

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 で実行中または同じ

スレッド06で実行:

スレッド011で実行: