其他
C/C++ 构建过慢时:用缓存、PCH、Unity…
当 C/C++ 项目编译或链接太慢,需要诊断瓶颈并用缓存、预编译头、合并编译、调试信息拆分、分布式编译和头文件修剪来提速时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
C/C++ 构建过慢时:用缓存、PCH、通用
解决思路
- 先诊断瓶颈:运行 `time cmake --build build -j$(nproc)` 给完整构建计时。
- 找出最慢的 TU:使用 CMake ≥3.16,执行 `cmake -S . -B build -DCMAKE_CXX_FLAGS="-ftime-report"`,再执行 `cmake --build build 2>&1 | grep "Total" | sort -t: -k2 -rn | head -20`。
- 查看 Ninja 构建耗时:用串行计时 `ninja -C build -j1 2>&1 | grep "^\[" | sort -t" " -k2 -rn | head -20`。
- 安装 ccache:Ubuntu/Debian 执行 `apt-get install ccache`,macOS 执行 `brew install ccache`。
- 查看 ccache 命中率:执行 `ccache -s`。
- 配置 ccache 缓存大小(默认 5GB):执行 `ccache -M 20G`。
- 需要时清空 ccache 缓存:执行 `ccache -C`。
- CMake 集成 ccache(推荐,优于前缀 hack):在 `CMakeLists.txt` 中执行 `find_program(CCACHE_PROGRAM ccache)`,若找到则设置 `CMAKE_C_COMPILER_LAUNCHER` 和 `CMAKE_CXX_COMPILER_LAUNCHER` 为 `${CCACHE_PROGRAM}`。
- 配置 `~/.config/ccache/ccache.conf`:设置 `max_size = 20G`、`compression = true`、`compression_level = 6`;CI 可设置 `cache_dir = /shared/ccache` 跨 job 共享缓存。
- 安装 sccache:执行 `cargo install sccache`,或执行 `brew install sccache`。
- 将 sccache 设为编译器启动器:Rust 用 `export RUSTC_WRAPPER=sccache`,CMake 用 `export CMAKE_C_COMPILER_LAUNCHER=sccache`。
- 使用 sccache 的 S3 后端:设置 `SCCACHE_BUCKET=my-build-cache`、`SCCACHE_REGION=us-east-1`,并执行 `sccache --start-server`。
- 查看 sccache 统计:执行 `sccache --show-stats`。
- 使用预编译头 PCH:让大而稳定的头文件编译一次并复用二进制形式。
- CMake ≥3.16 原生 PCH:使用 `target_precompile_headers(mylib PRIVATE <vector> <string> <unordered_map> "myproject/common.h")`。
- 跨 target 共享 PCH:使用 `target_precompile_headers(myapp REUSE_FROM mylib)` 避免重复编译。
- 传统 `stdafx.h` / `pch.h` 方式:所有 TU 将 `pch.h` 作为第一个 include;`pch.h` 用 `#pragma once` 并包含重量级系统头如 `#include <stdio.h>`、`#include <stdlib.h>`、`#include <string.h>`。
- PCH 最适用于大型稳定头文件(STL、Boost、Qt);避免对频繁变动的项目头文件使用 PCH。
- Unity/jumbo 构建:合并多个 `.cpp` 文件为一个 TU,减少头文件解析开销并改善内联。
- CMake ≥3.16 unity build:执行 `set_target_properties(mylib PROPERTIES UNITY_BUILD ON)`。
- 控制每 unity TU 批大小(默认 8):执行 `set_target_properties(mylib PROPERTIES UNITY_BUILD_BATCH_SIZE 16)`。
- 将特定文件排除出 unity(如 ODR 问题):执行 `set_source_files_properties(problem.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)`。
- 手动 unity 文件:创建 `unity_build.cpp`,在其中写 `#include "module_a.cpp"`、`#include "module_b.cpp"`、`#include "module_c.cpp"`。
- Unity 注意事项:匿名命名空间在每个 TU 中独立;避免头文件中 `using namespace`;警惕重复静态变量。
- 使用 split-DWARF 减少链接时间:将调试信息放入 `.dwo` 附属文件,显著减少链接器处理量。
- GCC/Clang 启用 split-DWARF:执行 `gcc -g -gsplit-dwarf -o prog main.c`。
- CMake 全局启用 split-DWARF:执行 `add_compile_options(-gsplit-dwarf)`。
- 可选合并 `.dwo` 文件用于分发:执行 `dwp -o prog.dwp prog`(GNU dwp 工具)。
- 配合 `--gdb-index` 加快 GDB 启动:执行 `gcc -g -gsplit-dwarf -Wl,--gdb-index -o prog main.c`。
- 了解 split-DWARF 收益:大型项目典型链接时间,`-g` 完整 DWARF 比 `-gsplit-dwarf` 长约 4–6 倍。
- 使用 distcc 分布式编译:在所有机器执行 `apt-get install distcc`。
- 在 worker 机器启动 distcc 守护进程:执行 `distccd --daemon --allow 192.168.1.0/24 --jobs 8`。
- 客户端设置 `DISTCC_HOSTS`:例如 `export DISTCC_HOSTS="localhost/4 worker1/8 worker2/8"`,然后执行 `make -j20 CC="distcc gcc"`。
- CMake 集成 distcc:设置 `CMAKE_C_COMPILER_LAUNCHER distcc` 和 `CMAKE_CXX_COMPILER_LAUNCHER distcc`。
- 与 ccache 叠加:使用 `CC="ccache distcc gcc"`,让 ccache 先查本地缓存,未命中再回退到 distcc。
- 用 IWYU 做 include 修剪:执行 `apt-get install iwyu`。
- 通过 CMake 运行 IWYU:执行 `cmake -S . -B build -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE=iwyu`,再执行 `cmake --build build 2>&1 | tee iwyu.log`。
- 自动应用 IWYU 修复:执行 `fix_include < iwyu.log --nosafe_headers`。
- 完整 IWYU 工作流见 `skills/build-systems/include-what-you-use`;ccache 配置选项见 `references/ccache-config.md`。
- 相关技能:CMake 项目结构见 `skills/build-systems/cmake`;Rust 构建加速见 `skills/rust/rust-build-times`;split-DWARF 内部机制见 `skills/debuggers/dwarf-debug-format`。
适用边界 · 注意事项
- PCH 不适用于频繁改动的项目头文件;它最适合大型且稳定的头文件(STL、Boost、Qt)。
- Unity build 在存在匿名命名空间、头文件中的 `using namespace`、重复静态变量或 ODR 问题时不要直接全量启用,需排除问题文件。
- distcc 需要多台可信/同构机器和网络;sccache 的 S3 后端需要对象存储/云账号;若只有单机且无共享缓存,这些部分收益有限或不适用。