Approach overview
I’d attack build-time from three angles: reduce what needs compiling, make compiling cheaper/faster, and speed developer iteration with runtime reloads. For game projects those map to header hygiene + partitioning, build-distribution & caching, and hot-reload/live coding.
Concrete techniques
- Header hygiene & IWYU: enforce “include what you use”, replace heavy headers with forward-decls, move inline impl to .cpp. Result: fewer translation-unit dependencies.
- Precompiled headers (PCH): place stable, heavy headers (STL, engine core, third-party libs) in a PCH used by most TUs. Great for repeated includes (renderer headers); keep PCH contents stable to avoid frequent rebuilds.
- Unity builds (jumbo files): concatenate many small .cpp files into one translation unit to amortize parser/compile cost. Use for gameplay code where ABI isolation isn’t required. Example: group per module (gameplay_unity.cpp contains 128 .cpp includes).
- Module partitioning: split codebase into coarse subsystems (renderer, physics, gameplay, tools) with clear public interfaces so only touched modules recompile. For C++20 modules, migrate stable APIs to modules to avoid header parsing.
- Distributed builds & caching: use sccache (or clang’s cache) + icecc or Build Accelerator to parallelize across dev machines/CI. sccache reduces repeated work; icecc speeds raw compile distribution.
- Link-time considerations: enable incremental linking where possible; for large unity builds consider using LTO selectively and limit unity size to bound link-time blowup.
- Hot-reload / live-coding: implement DLL-based plugin system or use engine live-coding (Unreal Live Coding). For gameplay iteration, compile small modules as reloadable DLLs so authors can test without full restart. For data-driven parts, reload assets/scripts at runtime.
Trade-offs & mitigations
- Unity builds reduce compile time but increase link time and can hide ODR/initialization bugs. Mitigate by grouping by module and running periodic full-builds/unity-disabled CI jobs.
- Large PCHs invalidate many TUs when changed. Keep PCH stable and split into common + rarely-changed blocks.
- Distributed builds require fast network and consistent environment; use containerized toolchains to avoid toolchain drift.
- Hot-reload adds complexity and can mask lifetime/ABI bugs. Use it for gameplay loops, not low-level engine refactors; keep robust versioned plugin interfaces.
Practical rollout plan
- Measure current build (hot paths, CI vs local).
- Apply header hygiene + IWYU linter.
- Add PCH and cherry-pick module partitioning (render/physics/gameplay).
- Pilot unity builds on gameplay module and enable sccache + icecc in CI.
- Add DLL-based hot-reload for gameplay, measure iteration time reduction, iterate on limits and CI checks.
This combination yields large iteration savings for gameplay devs while keeping engine stability via periodic full-builds and CI gates.