C誤った共有の例
誤った共有は、複数のスレッドが同じキャッシュラインの異なる変数を変更し、キャッシュの故障とパフォーマンスの劣化をもたらすと発生します。 1.構造塗りつぶしを使用して、各変数を1つのキャッシュラインのみを占めるようにします。 2。メモリアライメントにalignasまたはstd :: hardware_destructive_interference_sizeを使用します。 3.スレッドローカル変数を使用して最終的に結果をマージし、それにより擬似共有を回避し、マルチスレッドプログラムのパフォーマンスを改善します。
マルチスレッドプログラミングでは、誤った共有は一般的なパフォーマンストラップです。複数のスレッドが同じキャッシュラインにある異なる変数を変更し、不必要なキャッシュの故障とパフォーマンスの劣化をもたらすと発生します。開発者はメモリレイアウトを直接制御できるため、この問題はCで特に発生する傾向があります。

これは、典型的なC虚偽共有の例とそれを修正する方法です。
?偽の共有とは何ですか?
最新のCPUキャッシュ「キャッシュライン」(通常64バイト)にデータをロードします。 2つの変数が同じキャッシュラインにある場合、異なるスレッドで独立してアクセスされた場合でも、1つのスレッドの書き込みにより、別のスレッドのキャッシュラインが無効になるため、メモリからのリロードが強制されます。これは擬似共有です。

?擬似共有サンプルコード
#include <thread> #include <vector> #include <Chrono> #include <iostream> alignas(64)struct counter { 揮発性長い値= 0; }; const int num_threads = 4; const long Iterations = 10'000'000; //複数のスレッドは配列を共有しますが、各スレッドは異なる要素で動作しますstd :: vector <counter> counters_bad(num_threads); void worker_bad(int tid){ for(long i = 0; i <iterations; i){ counters_bad [tid] .value; } } int main(){ std :: vector <std :: thread> threads; auto start = std :: chrono :: high_resolution_clock :: now(); for(int i = 0; i <num_threads; i){ threads.emplace_back(worker_bad、i); } for(auto&t:threads){ T.Join(); } auto end = std :: chrono :: high_resolution_clock :: now(); Auto duration = std :: Chrono :: Duration_cast <std :: Chrono :: milliseconds>(end -start); std :: cout << "bad(false共有):" << duration.count()<< "ms \ n"; 0を返します。 }
⚠️各スレッドは
counters_bad[tid]
を動作させますが、Counter
構造はalignas(64)
のみを使用して個々の変数を整列させ、std::vector
の要素は継続的に保存されます。Counter
が十分に満たされていない場合、複数のCounter
同じキャッシュラインに絞り、擬似共有を引き起こす可能性があります。
しかし、上記のコードは実際には十分に一般的ではありません。より明確な比較を書きましょう。

?比較:擬似共有と擬似共有はありません
#include <thread> #include <vector> #include <Chrono> #include <iostream> //方法1:誤った共有を生成するのは簡単です(変数が近すぎます) struct counterfalse { 揮発性長いa = 0; //パディングなしでは、複数のカウンターファルスがキャッシュラインを共有する場合があります}; //方法2:誤った共有を避け、手動でそれをキャッシュラインstructカウンター固定{に入力します{ 揮発性長いa = 0; Char Padding [64 -sizeof(long)]; // 64バイトに入力}; const int num_threads = 4; const long Iterations = 10'000'000; //テスト1:false共有バージョンstd :: vector <CounterFalse> counters_false(num_threads); void worker_false(int tid){ for(long i = 0; i <iterations; i){ counters_false [tid] .a; } } //テスト2:偽の共有バージョンstd :: vector <Counterfixed> counters_fixed(num_threads); void worker_fixed(int tid){ for(long i = 0; i <iterations; i){ counters_fixed [tid] .a; } } int main(){ std :: vector <std :: thread> threads; //誤った共有をテストします auto start = std :: chrono :: high_resolution_clock :: now(); for(int i = 0; i <num_threads; i){ threads.emplace_back(worker_false、i); } for(auto&t:threads)t.join(); auto end = std :: chrono :: high_resolution_clock :: now(); auto duration1 = std :: chrono :: duter_cast <std :: chrono :: milliseconds>(end -start); std :: cout << "false共有:" << duration1.count()<< "ms \ n"; threads.clear(); //固定バージョンをテストします start = std :: chrono :: high_resolution_clock :: now(); for(int i = 0; i <num_threads; i){ threads.emplace_back(worker_fixed、i); } for(auto&t:threads)t.join(); end = std :: chrono :: high_resolution_clock :: now(); auto duration2 = std :: chrono :: duter_cast <std :: chrono :: milliseconds>(end -start); std :: cout << "false共有なし:" << duration2.count()<< "ms \ n"; 0を返します。 }
?出力の例(マルチコアマシン上)
誤った共有で:850ミリ秒 誤った共有なし:220ミリ秒
擬似共有を回避した後、パフォーマンスの改善が重要であることがわかります(約4倍) 。
?誤った共有を避ける方法は?
- パディングの使用:各スレッドが変数がキャッシュラインのみを占めることを確認してください(通常64バイト)。
-
alignas(64)
:構造または変数の力のアライメント。 - eachスレッドはローカル変数を使用し、最後にマージします。共有変数アクセスを減らします。
- 標準ライブラリによって提供されるアライメントツールを使用します(C 11の
alignas
やstd::hardware_destructive_interference_size
など)
? C 17は、推奨される2つの定数を導入します。
std::hardware_destructive_interference_size
:擬似共有を避けるための最小間隔(通常はキャッシュラインサイズ)std::hardware_constructive_interference_size
:共有の推奨サイズ
例(C 17から):
struct alignas(std :: hardware_destructive_interference_size)counter { 揮発性長い値= 0; };
?要約します
- 誤った共有は無害に見えますが、実際にはマルチスレッドプログラムを大幅に遅くしています。
- 複数のスレッドが「論理的に独立しているが物理的に近い」変数を頻繁に書き込む場合、トリガーが簡単です。
- 回避策:変数が同じキャッシュライン上にないことを確認するためのメモリパディングアライメント。
- この問題は、パフォーマンスに敏感な並列プログラム(高周波カウント、同時統計など)で注意を払う必要があります。
基本的にこれはすべて複雑ではありませんが、無視するのは簡単です。
以上がC誤った共有の例の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undress AI Tool
脱衣画像を無料で

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ソフトウェアまたはゲームを開くと、「アプリケーションが正常に開始できない(0xc0000906)」が表示され、多くのユーザーが混乱し、どこから始めればよいかわからないというプロンプトが突然表示されます。実際、これらのエラーのほとんどは、システムファイルの破損またはランタイムライブラリの欠落によって引き起こされます。急いでシステムを再インストールしないでください。この記事では、いくつかのシンプルで効果的なソリューションを提供して、プログラムを迅速に復元するのに役立ちます。 1. 0xc0000906のエラーは何ですか?エラーコード0xc0000906は、Windowsシステムの一般的な起動例の例外です。これは通常、プログラムが実行中に必要なシステムコンポーネントや実行環境をロードできないことを意味します。この問題は、大規模なソフトウェアやゲームを実行するときに発生することがよくあります。主な理由には、必要なランタイムライブラリがインストールまたは破損していないことが含まれます。ソフトウェアインストールパッケージは無限です

コンピューターは「MSVCP71.DLLがコンピューターから欠落している」とプロンプトします。これは通常、システムに重要な実行コンポーネントがないため、ソフトウェアが正常にロードされないためです。この記事では、ファイルの機能とエラーの根本原因を深く分析し、3つの効率的なソリューションを提供して、プログラムを迅速に実行するのに役立ちます。 1。MSVCP71.dllとは何ですか? MSVCP71.DLLは、Microsoft VisualC 2003のコアランタイムライブラリファイルに属し、Dynamic Link Library(DLL)タイプに属します。これは、主に標準関数、STLテンプレート、および基本的なデータ処理モジュールを呼び出すためにCで記述されたプログラムをサポートするために使用されます。 2000年代初頭に開発された多くのアプリケーションとクラシックゲームは、このファイルに依存して実行されます。ファイルが欠落または破損したら、

Cで正規表現を使用するには、ヘッダーファイルを含めて、パターンマッチングとテキスト処理に提供する機能を使用する必要があります。 1。STD:: regex_matchを使用して完全な文字列に一致し、文字列全体がパターンに準拠している場合にのみtrueを返します。 2。STD:: regex_searchを使用して、文字列の任意の位置で一致を見つけます。 3。STD:: SMATCHを使用してキャプチャグループを抽出し、マッチ[0]、マッチ[1]、およびその後のサブマッチを介して完全な一致を取得します。 4。STD:: regex_replaceを使用して一致するテキストを置き換え、1ドルや2ドルなどの参照でキャプチャグループをサポートします。 5.正規表現を構築するときにISETを追加できます(

Cでのオペレーターの過負荷により、標準演算子の新しい動作をカスタムタイプに割り当てることができます。1。メンバー関数の過負荷を介して新しいオブジェクトを返します。 2。オーバーロード=現在のオブジェクトを変更し、参照を返します。 3。フレンド関数のオーバーロード

Cでは、STD :: MAPおよびSTD :: UNORDERED_MAPの選択は、特定の要件に依存します。 1。根底にある異なる構造:STD :: MAPは赤と黒の木に基づいて実装され、キーは順番、デフォルトの昇順、および検索と挿入の複雑さはo(logn)です。 std :: unordered_mapはハッシュテーブルを使用し、順序ではなく、検索と挿入の平均複雑さはo(1)であり、最悪はo(n)です。 2。挿入性能とメモリオーバーヘッド:マップ挿入には、ツリー構造のメンテナンスが必要であり、効率が低くなります。 UNORDERED_MAPの挿入はより速くなりますが、より多くのメモリを消費し、Reserve()を通じて最適化できます。 3。カスタム比較関数:マップはカスタム比較関数をサポートしています。

std :: vectorの基本的な使用には、次のものが含まれます。1。ベクトルを宣言します。 2. push_back()で要素を追加します。 3。初期化リストで初期化。 4。範囲のループトラバーサル。 5。インデックスまたはback()を介して要素にアクセスします。 6。要素を変更するための値の直接割り当て。 7。fop_back()でエンド要素を削除します。 8。SIZE()を呼び出して、要素の数を取得します。 Constautoを使用し、コピーを避け、リザーブを事前に挿入してパフォーマンスを改善し、アクセス前に空でないことを確認することをお勧めします。このデータ構造は、文字列リストを処理する効率的で好ましい方法です。

STD :: Variantは、C 17によって導入されたタイプセーフユニオンです。指定されたタイプの1つの値を安全に保持できます。 STD :: get、std :: holds_alternative、std :: std :: get_ifなどのメソッドを介した安全なアクセスとタイプチェックを実現できます。 STD ::単一型と組み合わせて、オプションの値をシミュレートできます。 STD ::タイプ分布のためにアクセスし、メンテナンス性を向上させるために大きなタイプのリストを避け、最終的にタイプの安全性と例外の安全性を確保することをお勧めします。

abasicmakefileautomatesc compilation bydefining withtargets、依存関係、およびコマンド
