Build Time Optimization
C++ build time optimization — precompiled headers, unity builds, modules, forward declarations, include hygiene, ccache, and CMake techniques.
TL;DR
Large C++ projects can take minutes to build. Key levers: precompiled headers (PCH), unity builds, forward declarations, include-what-you-use, ccache, and eventually C++20 modules. Profile the build before optimizing.
Profile the Build First
# CMake Ninja: show build time per file
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo ..
ninja -j4 -v 2>&1 | ts '%.s' | head -100
# Clang: time each translation unit
clang++ -ftime-trace main.cpp # generates main.cpp.json
# Open in Chrome DevTools (chrome://tracing) or speedscope.appPrecompiled Headers (PCH)
PCH compiles heavy headers once and reuses the binary:
# CMake 3.16+ native PCH
target_precompile_headers(myapp PRIVATE
<vector>
<string>
<unordered_map>
<memory>
<algorithm>
"include/common.hpp"
)
# Share PCH across targets (avoids recompiling it for each)
target_precompile_headers(myapp REUSE_FROM mylib)// pch.hpp — include expensive headers here
#pragma once
#include <vector>
#include <string>
#include <unordered_map>
#include <memory>
#include <algorithm>
#include <functional>
// Project-wide types
#include "types.hpp"
#include "logging.hpp"Unity Builds (Jumbo Builds)
Combines multiple .cpp files into one translation unit — reduces redundant parsing:
# CMake 3.16+ unity builds
set_target_properties(myapp PROPERTIES UNITY_BUILD ON)
# Fine-grained control
set_target_properties(myapp PROPERTIES
UNITY_BUILD ON
UNITY_BUILD_BATCH_SIZE 16 # combine up to 16 .cpp files
)
# Exclude files that have ODR or static variable issues
set_source_files_properties(src/globals.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)Warning: Unity builds can break code that relies on internal linkage (static functions, anonymous namespaces with same-named symbols).
Forward Declarations
Remove #include from headers — use forward declarations instead:
// BAD: widget.hpp including everything
#include <vector>
#include <string>
#include "canvas.hpp" // heavyweight
// GOOD: forward declare, include in .cpp only
class Canvas; // forward declaration — no #include needed in header
class Widget {
Canvas* canvas_; // pointer: ok with forward declaration
void draw(Canvas&); // reference param: ok with forward declaration
// Canvas canvas_; // value member: needs full definition
};
// widget.cpp
#include "widget.hpp"
#include "canvas.hpp" // full include only where neededInclude What You Use (IWYU)
# Install: sudo apt install iwyu
# Run:
include-what-you-use -std=c++17 file.cpp -- -I include/
# With CMake:
set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "include-what-you-use;-Xiwyu;--no_comments")IWYU tells you which includes are used vs redundant, reducing unnecessary parsing.
Reducing Template Instantiations
// Explicit instantiation: instantiate once, use everywhere
// In template.hpp — declare, don't instantiate:
extern template class std::vector<MyType>;
// In template.cpp — force instantiation here only:
template class std::vector<MyType>;
// All other TUs link against this, skip generating the codeccache (Compiler Cache)
Caches compiled objects — huge win on rebuilds:
# Install
sudo apt install ccache
# CMake integration
cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ..
# Check stats
ccache --show-stats
# Reset
ccache --clearC++20 Modules (Future)
// math.cppm — module interface
export module math;
export double add(double a, double b) { return a + b; }
export double square(double x) { return x * x; }
// main.cpp — import module (no header, no repeated parsing)
import math;
import std; // std library as module (C++23)
int main() {
std::println("{}", add(1.0, 2.0));
}# CMake 3.28+ modules support
target_sources(myapp
PUBLIC FILE_SET CXX_MODULES FILES
src/math.cppm
)Summary: Impact vs Effort
| Technique | Build speedup | Effort | Notes |
|---|---|---|---|
| ccache | 5–20× on rebuild | Low | Install + cmake flag |
| PCH | 2–5× | Low | CMake 3.16 built-in |
| Forward declarations | 1.5–3× | Medium | Requires header discipline |
| IWYU | 1.5–2× | Medium | Iterative cleanup |
| Unity builds | 2–4× | Low | Watch for ODR issues |
| C++20 modules | 5–10× | High | Toolchain maturity varies |