フォルス・シェアリング

インテル® VTune™ プロファイラー

この記事は、インテル® デベロッパー・ゾーンに公開されている『Intel® VTune™ Profiler Performance Analysis Cookbook』の「False Sharing」日本語参考訳です。原文は更新される可能性があります。原文と翻訳文の内容が異なる場合は原文を優先してください。


このレシピは、インテル® VTune™ Amplifier の全般解析とメモリーアクセス解析を使用してメモリー依存の linear_regression アプリケーションをプロファイルします。

コンテンツ・エキスパート: Dmitry Ryabtsev (英語)


全般解析は、インテル® VTune™ Amplifier 2019 でマイクロアーキテクチャー全般解析に改名されました。

使用するもの

以下は、パフォーマンス解析シナリオで使用するハードウェアとソフトウェアのリストです。

  • アプリケーション: linear_regressionlinear_regression.tgz サンプルパッケージは、製品の <install-dir>/samples/en/C++ ディレクトリーに含まれています。https://github.com/kozyraki/phoenix/tree/master/sample_apps/linear_regression (英語) からダウンロードすることもできます。
  • パフォーマンス解析ツール:
    • インテル® VTune™ Amplifier 2018: 全般解析、メモリーアクセス解析


      • インテル® VTune™ プロファイラー評価版のダウンロードと製品サポートについては、https://www.isus.jp/intel-vtune-amplifier-xe/ を参照してください。
      • このクックブックのレシピはすべてスケーラブルであり、インテル® VTune™ Amplifier 2018 以降に適用できます。バージョンにより設定がわずかに異なることがあります。
      • ベータ版インテル® oneAPI ベース・ツールキット向けのバージョンから、インテル® VTune™ Amplifier の名称がインテル® VTune™ プロファイラーに変わりました。引き続き、インテル® Parallel Studio XE またはインテル® System Studio のコンポーネントとして、あるいはスタンドアロン版のインテル® VTune™ プロファイラーをご利用いただけます。
  • オペレーティング・システム: Ubuntu* 16.04 64 ビット
  • CPU: インテル® Core™ i7-6700K プロセッサー

全般解析を実行する

サンプル・アプリケーションの潜在的なパフォーマンス・ボトルネックを理解するため、まず、インテル® VTune™ Amplifier の全般解析を実行します。

  1. ツールバーの [New Project (新規プロジェクト)] ボタンをクリックして、新規プロジェクトの名前 (例: linear_regression) を指定します。
  2. [Analysis Target (解析ターゲット)] ウィンドウで、ホストベースの解析として [local host (ローカルホスト)] ターゲット・システム・タイプを選択します。
  3. [Launch Application (アプリケーションを起動)] ターゲットタイプを選択して、右ペインで解析するアプリケーションを指定します。
  4. 右の [Choose Analysis (解析の選択)] ボタンをクリックし、[Microarchitecture Analysis (マイクロアーキテクチャー解析)] > [General Exploration (全般)] を選択して、[Start (開始)] をクリックします。

インテル® VTune™ Amplifier は、アプリケーションを起動してデータを収集し、収集したデータをファイナライズして、シンボル情報を解決します。この情報は、ソース解析で必要になります。

ボトルネックを特定する

ハードウェア・メトリックごとのアプリケーション・レベルの統計が表示される [Summary (サマリー)] ビューから始めます。

一般に、パフォーマンス解析では、ベースラインを作成して以降の最適化を測定することを推奨します。このケースでは、アプリケーションの [Elapsed Time (経過時間)] をベースラインとして使用します。

サマリーメトリックから、メモリーアクセスの競合によりアプリケーションのパフォーマンスが制限されていることが分かります。

競合するデータ構造を見つける

[Contested Accesses (アクセス競合)] メトリックの値が高い原因を調べるため、[Analyze dynamic memory objects (動的メモリー・オブジェクトを解析)] オプションを有効にしてメモリーアクセス解析を実行します。この解析は、競合問題の原因になっているデータ構造へのアクセスを見つけるのに役立ちます。

[Summary (サマリー)] ビューから、ファイル stddefines.h の行 52 のメモリー割り当てデータ・オブジェクトでアプリケーション実行のレイテンシーが高くなっていることが分かります。割り当てのサイズは 512 バイトと非常に小さいため、L1 キャッシュに完全に収まるはずです。詳細を確認するため、このオブジェクトをクリックして [Bottom-up (ボトムアップ)] ビューに切り替えます。

このオブジェクトの平均アクセス・レイテンシーは 59 サイクルと、L1 キャッシュ上にあると予想されるメモリーサイズとしては非常に高い値になっています。これがアクセス競合パフォーマンス問題の原因になっている可能性があります。

グリッドの stddefines.h:52 (512B) メモリー・オブジェクトを展開して割り当てスタックを表示します。割り当てスタックをダブルクリックして [Source (ソース)] ビューを開きます。オブジェクトが割り当てられているコード行がハイライトされます。

lreg_args の内容を次に示します。

typedef struct
{
    pthread_t tid;
    POINT_T *points;
    int num_elems;
    long long SX;
    long long SY;
    long long SXX;
    long long SYY;
    long long SXY;
} lreg_args;

次のように、lreg_args 配列にアクセスしているコードをスレッド化します。

// ADD Up RESULTS
for (i = 0; i < args->num_elems; i++)
{
    //Compute SX, SY, SYY, SXX, SXY
    args->SX  += args->points[i].x;
    args->SXX += args->points[i].x*args->points[i].x;
    args->SY  += args->points[i].y;
    args->SYY += args->points[i].y*args->points[i].y;
    args->SXY += args->points[i].x*args->points[i].y;
}

各スレッドは別々に配列の要素にアクセスしているため、フォルス・シェアリング問題が考えられます。

サンプルの lreg_args 構造のサイズは 64 バイトで、キャッシュラインのサイズと一致しています。しかし、これらの構造の配列を割り当てるときに、この配列が 64 バイトでアライメントされる保証はありません。その結果、配列要素がキャッシュライン境界を超えて、意図しない競合問題 (フォルス・シェアリング) が発生することがあります。

フォルス・シェアリング問題を修正する

このフォルス・シェアリング問題を修正するため、メモリーを 64 バイト・アライメントで割り当てる _mm_malloc 関数に変更します。

再コンパイルしてインテル® VTune™ プロファイラーのアプリケーション解析を再度実行すると、結果は次のようになりました。

Elapsed Time (経過時間) は 0.5 秒になり、オリジナルの 3 秒からパフォーマンスが大幅に向上しました。メモリー依存のボトルネックが解消し、フォルス・シェアリング問題が修正されました。


このレシピの情報は、デベロッパー・フォーラム (英語) を参照してください。

関連情報

インテル® VTune™ プロファイラー・パフォーマンス解析クックブックのトップに戻る

タイトルとURLをコピーしました