次の方法で共有


チュートリアル: ビルド時のヘッダー ファイルの影響のトラブルシューティング

Build Insights の[インクルード ファイル] および [インクルード ツリー] ビューを使用して、C と C++ のビルド時の #include ファイルの影響をトラブルシューティングします。

前提条件

  • Visual Studio 2022 17.8 以降
  • Visual Studio インストーラーを使用して C++ によるデスクトップ開発ワークロードをインストールする場合、C++ Build Insights は既定で有効になります。

C++ によるデスクトップ開発ワークロードが選択されている Visual Studio インストーラーのスクリーンショット。

インストールされているコンポーネントの一覧が表示されています。 C++ Build Insights が強調表示され、選択されています。これはインストール済みであることを意味します。

または、C++ によるゲーム開発ワークロードの場合:

C++ によるゲーム開発ワークロードが選択されている Visual Studio インストーラーのスクリーンショット。

インストールされているコンポーネントの一覧が表示されています。 C++ Build Insights が強調表示され、選択されています。これはインストール済みであることを意味します。

概要

Visual Studio に統合された Build Insights は、ビルド時間を最適化するのに役立ちます。特に、AAA ゲームなどの大規模なプロジェクトの場合に役立ちます。 大きなヘッダー ファイルが解析されるとき、特に繰り返し解析される場合は、ビルド時間に影響があります。

Build Insights によって、[インクルード ファイル] ビューで分析が行われます。これは、プロジェクト内の #include ファイルの解析の影響を診断するのに役立ちます。 各ヘッダー ファイルの解析にかかる時間と、ヘッダー ファイル間のリレーションシップのビューが表示されます。

この記事では、Build Insights の [インクルード ファイル][インクルード ツリー] ビューを使用して、解析するのに最も負荷の高いヘッダー ファイルを特定する方法と、プリコンパイル済みヘッダー ファイルを作成してビルド時間を最適化する方法について説明します。

ビルド オプションの設定

Build Insights データを収集する前に、測定するビルドの種類のビルド オプションを設定します。 たとえば、x64 デバッグ ビルド時間に関心がある場合は、[デバッグ][x64] のビルドを設定します。

  • [ソリューション構成] ドロップダウンで、[デバッグ] を選択します。

  • [ソリューション プラットフォーム] ドロップダウンで、[x64] を選択します。

    [ソリューション構成] ドロップダウンのスクリーンショット。

    [ソリューション構成] ドロップダウンが表示されます。 デバッグ、リリース、および構成マネージャーのオプションがあります。 [ソリューション プラットフォーム] ドロップダウンは x64 に設定されています。

Build Insights の実行

選択したプロジェクトで、前のセクションで設定した [デバッグ] ビルド オプションを使用して、メイン メニューから[ビルド]>[選択範囲に Build Insights を実行]>[リビルド] の順に選択して Build Insights を実行します。 ソリューション エクスプローラーでプロジェクトを右クリックし、[Build Insights の実行]>[リビルド] を選択することもできます。 少数のファイルではなく、プロジェクト全体のビルド時間を測定するのに [ビルド] ではなく、[リビルド] を選択すると、現時点ではファイルがダーティーになる可能性があります。

[選択範囲に Build Insights を実行] > [リビルド] が選択されているメイン メニューのスクリーンショット。

ビルドが完了すると、イベント トレース ログ (ETL) ファイルが開きます。 Windows TEMP 環境変数が指すフォルダーに保存されます。 生成された名前は、収集時刻に基づいています。

[インクルード ファイル] ビュー

トレース ファイルにはビルド時間が示されています。この例では 16.404 秒でした。 [診断セッション] は、Build Insights セッションの実行にかかった全体的な時間です。 [インクルード ファイル] タブを選択します。

このビューには、#include ファイルの処理に費やされた時間が表示されます。

[インクルード ファイル] ビューのスクリーンショット。

ファイル パス列で、火のアイコンを含む複数のファイルが強調表示されています。解析にビルド時間のうちの 10% 以上かかるためです。 winrtHeaders.h は、16.404 秒のビルド時間のうち 8.581 秒、つまり 52.3% で最大のものです。

[ファイル パス] 列のいくつかのファイルには、ビルド時間のうち 10% 以上かかることを示す火のアイコンが横にあります。

[時間 [秒, %]] 列には、ウォール クロック責任時間 (WCTR) で、各関数のコンパイルにかかった時間が表示されます。 このメトリックによって、並列スレッドの使用に基づいて、ファイルの解析にかかるウォール クロック時間が分散されます。 たとえば、2 つの異なるスレッドが 1 秒間に 2 つの異なるファイルを同時に解析している場合、各ファイルの WCTR は 0.5 秒として記録されます。 これによって、並列実行中にそれぞれ消費されたリソースを考慮に入れて、合計コンパイル時間に対する各ファイルの比例配分が反映されます。 このため、WCTR によって、複数のコンパイル アクティビティが同時に発生する環境で、各ファイルが全体的なビルド時間に与える影響がより適切に測定されます。

[解析数] 列には、ヘッダー ファイルが解析された回数が表示されます。

この一覧で強調表示されている最初のヘッダー ファイルは winrtHeaders.h です。全体的な 16.404 秒のビルド時間のうち 8.581 秒 (つまりビルド時間の 52.3%) かかります。 次に最も負荷がかかるのは Windows.UI.Xaml.Interop.h、次に Windows.Xaml.h です。

winrtHeaders.h が含まれるファイルを確認するには、その横にあるシェブロンをクリックします。 [解析数] 列は、ヘッダー ファイルが他のファイルに含まれる回数を示すことで役立ちます。 ヘッダー ファイルが複数回インクルードされていることがあります。これは、プリコンパイル済みヘッダー ファイルまたはリファクタリングの候補として適していることを示している場合があります。

[翻訳単位] 列には、インクルード ファイルが処理されたときに処理中だったファイルが表示されます。 この例では、Grapher.cpp のコンパイル中に winrtHeaders.h がインクルードされています。

[インクルード ファイル] ビューのスクリーンショット。

サンプル プロジェクトのインクルード ファイルを示す ETL ファイルの例。 ファイル パス列で、winrtHeaders.h が選択され、展開されています。 ビルドには 8.219 秒かかります。これはビルド時間の 50.1% です。 その子ノードは Grapher.cpp で、これも翻訳単位として一覧に表示されます。

翻訳単位列は、ヘッダー ファイルが何回もインクルードされた場合に、どのファイルがコンパイルされていたかを明らかにし、それが最も多く発生する場所を見つけたい場合に役立ちます。

winrtHeaders.h は解析に負荷がかかることがわかっていますが、詳細に調べることができます。

インクルード ツリー ビュー

このビューでは、子ノードとは親ノードに含まれるファイルのことです。 これは、ヘッダー ファイル間の関係を理解し、ヘッダー ファイルの解析回数を減らす機会を特定するのに役立ちます。

ETL ファイルの [インクルード ツリー] タブを選択して、[インクルード ツリー] ビューを表示します。

[インクルード ツリー] ビューのスクリーンショット。

プロジェクトのインクルード ツリーを表示します。 ファイル パス列に、他のファイルをインクルードする各ファイルが、インクルードされるファイルの数と解析時間と共に一覧表示されます。

このビューの [ファイル パス] 列には、他のファイルをインクルードする各ファイルが表示されます。 [インクルード数] には、このヘッダー ファイルにインクルードされるファイルの数が一覧表示されます。 このファイルを解析する時間が一覧表示され、展開されると、このヘッダー ファイルにインクルードされる個々のヘッダー ファイルを解析する時間が一覧表示されます。

以前は、winrtHeaders.h の解析には時間がかかることが分かっていました。 [ファイルのフィルター] テキスト ボックスに「winrtHeaders.h」と入力すると、名前に winrtHeaders.h が含まれるエントリのみにビューをフィルター処理できます。 winrtHeaders.h の横にあるシェブロンをクリックすると、インクルードされるファイルが表示されます。

展開された [インクルード ツリー] ビューのスクリーンショット。

ファイル パス列には、他のファイルをインクルードする各ファイルと、インクルードされるファイルの数と解析にかかった時間が一覧表示されます。winrtHeaders.h が選択され、展開され、インクルードされるファイルが表示されます。 Windows.UI.Xaml.Interop.h は、これらのファイルの 1 つであり、Windows.UI.Xaml.Interop.h が展開されて、インクルードされるヘッダー ファイルが表示されるように展開されます。

winrtHeaders.hWindows.UI.Xaml.Interop.h がインクルードされていることがわかります。 [インクルード ファイル] ビューから、これも解析に時間がかかることを思い出してください。 Windows.UI.Xaml.Interop.h の横にあるシェブロンをクリックすると、Windows.UI.Xaml.h がインクルードされていることがわかります。これには 21 個の他のヘッダー ファイルがインクルードされており、そのうちの 2 つがホット リストにあります。

解析する最も負荷の高いヘッダー ファイルの一部を特定し、winrtHeaders.h でそれらを取り込む役割を担っていることが確認されたら、プリコンパイル済みヘッダーを使用して、winrtHeaders.h をより迅速にインクルードすることができることを示します。

プリコンパイル済みヘッダーを使用してビルド時間を短縮する

[インクルード ファイル] ビューから、winrtHeaders.h の解析に時間がかかることがわかっています。また、[インクルード ツリー] ビューからは、winrtHeaders.h には解析に時間がかかる他のいくつかのヘッダー ファイルがインクルードされていることがわかっているため、プリコンパイル済みヘッダー ファイル (PCH) をビルドして、PCH の解析を 1 回だけ実行して高速化します。

pch.h を追加して winrtHeaders.h をインクルードします。これは次のようになります。

#ifndef CALC_PCH
#define CALC_PCH

#include <winrtHeaders.h>

#endif // CALC_PCH

PCH ファイルは、使用する前にコンパイルする必要があるため、pch.h をインクルードする任意の名前の pch.cpp ファイルをプロジェクトに追加します。 これには次の 1 行が含まれます。

#include "pch.h"

次に、PCH を使用するようにプロジェクトを設定します。 これは、[C/C++]>[プリコンパイル済みヘッダー] を使用してプロジェクト プロパティで行い、[プリコンパイル済みヘッダー][使用 (/Yu)] に設定し、[プリコンパイル済みヘッダー ファイル][pch.h] に設定します。

[プリコンパイル済みヘッダー] 設定が開いている [プロジェクトのプロパティ] ダイアログのスクリーンショット。

[プリコンパイル済みヘッダー] が次の値に設定されています: [使用 (/Yu)]。 [プリコンパイル済みヘッダー ファイル] が pch.h に設定されています。

PCH を使用するには、winrtHeaders.h を使用するソース ファイルの最初の行としてそれをインクルードします。 他のインクルード ファイルの前に置く必要があります。 または、わかりやすくするために、ソリューション内のすべてのファイルの先頭に pch.h をインクルードするようにプロジェクト プロパティを変更できます。これには、プロジェクト プロパティの [C/C++]>[詳細]>[強制インクルード ファイル]pch.h に設定します。

詳細設定が開いている [プロジェクトのプロパティ] ダイアログのスクリーンショット。

[強制インクルード ファイル] が pch.h に設定されています。

PCH には winrtHeaders.h がインクルードされているため、現在それが含まれるすべてのファイルから winrtHeaders.h を削除できます。 コンパイラは、winrtHeaders.h が既にインクルードされており、もう一度解析することはないと認識しているので、厳密には必要ありません。 一部の開発者はわかりやすくするため、または、PCH がリファクタリングされる可能性が高く、そのヘッダー ファイルがインクルードされなくなる場合があるため、ソース ファイルに #include を残すことを好みます。

変更内容をテストする

最初にプロジェクトをクリーンアップして、以前と同じファイルのビルドを比較していることを確認します。 1 つのプロジェクトのみをクリーンアップするには、ソリューション エクスプローラーでプロジェクトを右クリックし、[プロジェクトのみ]>[<プロジェクト名> のみをクリーン] を選択します。

このプロジェクトではプリコンパイル済みヘッダー (PCH) が使用されるようになったため、PCH のビルドに費やされる時間を測定する必要はありません。それは 1 回のみ行われるためです。 これを行うには、pch.cpp ファイルを読み込み、Ctrl + F7 キーを押して、そのファイルだけをビルドします。 ソリューション エクスプローラーで pch.cpp を右クリックし、Compile を選択して、このファイルをコンパイルすることもできます。

次に、ソリューション エクスプローラーで Build Insights を再実行します。そのためには、プロジェクトを右クリックし、[プロジェクトのみ]>[ビルド時に Build Insights を実行] の順に選択します。 ソリューション エクスプローラーでプロジェクトを右クリックし、[Build Insights の実行]>[ビルド] を選択することもできます。 今回、リビルドしないのは、そうすることによって測定したくない PCH がリビルドされるためです。 前にプロジェクトをクリーンしました。つまり、通常のビルドでは、測定するすべてのプロジェクト ファイルがコンパイルされます。

ETL ファイルが表示されると、ビルド時間が 16.404 秒から 6.615 秒になったことがわかります。 フィルター ボックスに「winrtHeaders.h」と入力すると、何も表示されません。 これは、プリコンパイル済みヘッダーによってプルされるため、解析に費やされた時間がごくわずかであるためです。

トレース ファイルの [インクルード ツリー] ペインのスクリーンショット。winrtHeaders が一覧に表示されなくなりました。

この例では、C++20 より前の一般的なソリューションであるため、プリコンパイル済みヘッダーを使用しています。 ただし、C++20 以降では、ヘッダー ユニットやモジュールなど、ヘッダー ファイルをインクルードする高速で安定した方法が他にもあります。 詳細については、「ヘッダー ユニット、モジュール、プリコンパイル済みヘッダーを比較する」を参照してください。

[インクルード ファイル][インクルード ツリー] ビューの両方に対して、いくつかのナビゲーション機能があります。

  • [インクルード ファイル] または [インクルード ツリー] のいずれかで 1 つのファイルをダブルクリックして (または Enter キーを押して)、そのファイルのソース コードを開きます。
  • 1 つのヘッダー ファイルを右クリックして、そのファイルを他方のビューで見つけます。 たとえば、[インクルード ファイル] ビューで、winrtHeaders.h を右クリックし、[インクルード ツリーで検索] を選択して、それを [インクルード ツリー] ビューに表示します。

[インクルード ファイル] ビューでファイルを右クリックしたスクリーンショット。[インクルード ツリー ビューで表示] メニュー オプションが強調表示されています。

または、[インクルード ツリー] ビューでファイルを右クリックして、[インクルード ファイル] ビューでファイルにジャンプすることもできます。

ヒント

  • [ファイル]>[名前を付けて保存] で ETL ファイルをより永続的な場所に保存して、ビルド時間の記録を保持できます。 その後、それを将来のビルドと比較して、変更によってビルド時間が改善されているかどうかを確認できます。
  • [Build Insights] ウィンドウを誤って閉じた場合は、一時フォルダー内の <dateandtime>.etl ファイルを見つけて再度開きます。 TEMP Windows 環境変数によって、一時ファイル フォルダーのパスが提供されます。
  • Windows パフォーマンス アナライザー (WPA) を使用して Build Insights データを調べるには、ETL ウィンドウの右下にある [WPA で開く] ボタンをクリックします。
  • 列をドラッグして、列の順序を変更します。 たとえば、[時間] 列を最初の列に移動することもできます。 列ヘッダーを右クリックし、表示しない列の選択を解除することで、列を非表示にすることができます。
  • [インクルード ファイル][インクルード ツリー] ビューには、関心のあるヘッダー ファイルを検索するためのフィルター ボックスが用意されています。 指定した名前に部分一致します。
  • ヘッダー ファイルについて報告される解析時間が、それをインクルードするファイルによって異なる場合があります。 これは、ヘッダーのどの部分が展開されているか、ファイル キャッシュ、およびその他のシステム要因に影響を与えるさまざまな #define の相互作用が原因である可能性があります。
  • [インクルード ファイル] または [インクルード ツリー] ビューで表示しようとしている内容を忘れた場合は、タブにカーソルを合わせると、ビューを説明するヒントが表示されます。 たとえば、[インクルード ツリー] タブにカーソルを合わせると、ヒントに "子ノードが親ノードに含まれるファイルであるすべてのファイルのインクルード統計情報を表示するビュー" と表示されます。
  • ヘッダー ファイルのすべての時間の集計期間がビルド全体の期間よりも長い (Windows.h など) 場合があります。 この場合、ヘッダーが同時に複数のスレッドで解析されています。 2 つのスレッドが 1 つのヘッダー ファイルの解析に同時に 1 秒を費やす場合、ウォール クロック時間が 1 秒しか経過していない場合でも、ビルド時間は 2 秒です。 詳細については、ウォール クロック責任時間 (WCTR) に関するページを参照してください。

トラブルシューティング

  • [Build Insights] ウィンドウが表示されない場合は、ビルドではなくリビルドを実行します。 実際に何もビルドされていない場合、[Build Insights] ウィンドウは表示されません。これは、前回のビルド以降にファイルが変更されていない場合に該当します。
  • 目的のヘッダー ファイルが [インクルード ファイル] または [インクルード ツリー] ビューに表示されない場合は、ビルドされなかったか、ビルド時間が一覧に表示するほど長くありません。

関連項目

ヘッダー ユニット、モジュール、プリコンパイル済みヘッダーを比較する
Visual Studio での Build Insights のビデオ - Pure Virtual C++ 2023
C++ ビルドの高速化、簡略化: 時間の新しいメトリック
チュートリアル: ビルド時の関数インライン化に関するトラブルシューティング
チュートリアル: vcperf および Windows パフォーマンス アナライザー