ビルド時間 — incremental build・PCH・Unity Build・C++でビルドが遅くなる理由¶
なぜC++のビルドは遅いのか(原因の理解が対策の半分)¶
- #includeの爆発:
#include <vector>だけで数万行が翻訳単位に貼り付く(プリプロセッサ)。100個の.cppが同じヘッダーを含めば、同じ数万行を100回パースする - テンプレートの再実体化:
vector<int>を使う全翻訳単位で実体化→リンカが重複を捨てる。「作っては捨てる」の山(→ 実体化) - 最適化・LTO: -O2の解析、リンク時最適化は全プログラム解析
- ヘッダー依存の連鎖: 1つのヘッダー変更が、それをincludeする全.cppの再コンパイルを誘発(変更の波及範囲=ビルド時間——結合度が物理現象になったもの)
対策1: incremental build(差分ビルド)を機能させる¶
- ビルドシステムは「変更されたファイル+その影響先」だけ再ビルドする(→ ビルドとは)。これが効かない構造にしてしまうのが問題:
- 「何でも入りヘッダー」(全域includeされる
common.hに頻繁に触る)→ 毎回全ビルド - ヘッダーに実装を書きすぎる → 変更のたび広範囲が再コンパイル
- 対策はヘッダー衛生: 前方宣言でincludeを減らす(翻訳単位)、実装は.cppへ、変更頻度の高いものを上流ヘッダーに置かない、includeの棚卸し(include-what-you-use)
対策2: PCH (Precompiled Header)¶
「ほぼ全員が使い、ほぼ変更されないヘッダー群」(標準ライブラリ、エンジンAPI)を1回だけコンパイルした状態で保存し、各翻訳単位はそこから開始する仕組み。
# CMake 3.16+
target_precompile_headers(game_core PRIVATE <vector> <string> <memory> "engine/core.h")
- 効果は大(体感数倍もある)。代償: PCH対象を変更すると全再ビルド/「PCHに入っているから」とincludeを書かない癖がつくと依存が見えなくなる(PCH無し環境で壊れる)
- C++20のモジュールが根本解決の方向だが、ツールチェーンの成熟待ちの過渡期(2020年代半ば時点)
対策3: Unity Build(Jumbo Build)¶
複数の.cppを #include で1つの巨大.cppにまとめてコンパイルする力技(Unityエンジンとは無関係。名前が同じだけなので注意)。
- 効果: ヘッダーの重複パースとテンプレート再実体化が激減(フルビルドが数分の一になる例も)。UEのUBTは既定でこれを行う(Adaptive Unity Build)
- 代償:
- 翻訳単位の分離が壊れる: 別ファイルの
static/無名namespaceが衝突、usingが漏れる——「Unity Buildでだけビルドが壊れる/通ってしまう」 - 差分ビルドが粗くなる(1ファイル変更でバッチ丸ごと再コンパイル)
- CMake:
set(CMAKE_UNITY_BUILD ON)
対策4: その他の実務手段¶
- Ninja(Makeより高速なスケジューリング)+並列数の適正化
- 分散ビルド(IncrediBuild、FASTBuild、sccache)——大規模スタジオの標準装備
- リンク時間: 分割ライブラリ、増分リンク、mold/lldなど高速リンカ
- 計測: Clangの
-ftime-trace、ninjaのログで「どのファイル・どのヘッダーが遅いか」を特定してから直す(ビルド時間も計測してから最適化)
C#/Unityとの対応¶
- C#のコンパイルが速い理由: ヘッダーがない(メタデータ参照)、テンプレート再実体化がない(ジェネリックは実行時)、パース対象が書いた分だけ——C++の遅さの原因が構造的に存在しない
- Unityで遅いのは主にドメインリロードとアセット処理で、コンパイル自体は速い。Assembly Definition分割は「変更の波及範囲を狭める」というこのページと同じ思考(→ 第9部)
- UEのビルドが遅いのは、まさにこのページの問題との戦い(UBTのPCH+Unity Build+モジュール分割は全部このページの対策の実装)
実務指針(優先順)¶
- 計測(どこが遅い?)
- ヘッダー衛生(前方宣言・include削減)——設計改善でもある
- PCH(手軽で効果大)
- Unity Build(効果大だが規律が要る)
- ハードウェア・分散(金で解決)
理解度チェック¶
- C++のビルドが遅い構造的理由を2つ(include爆発・テンプレート再実体化)説明できますか。
- PCHの効果と2つの代償は?
- Unity Buildが壊す「分離」とは何ですか。
演習¶
samples/cmake_demo/ に set(CMAKE_UNITY_BUILD ON) を試し、(a) フルビルド時間の変化(小規模なので差は小さい)、(b) 2つの.cppに同名のstatic関数を書いて衝突が起きることを確認してください。