如何使用Ccache加速c++项目的重复编译? (编译缓存工具)

ccache 能显著加速 C++ 项目重复编译,但需正确配置编译器路径、缓存目录、预处理器行为及 CMAKE_CXX_COMPILER_LAUNCHER 等关键项,否则可能失效或引发链接错误。

ccache 能显著加速 C++ 项目的重复编译,前提是正确配置编译器路径、缓存目录和预处理器行为;否则它可能完全不生效,甚至导致链接错误或缓存污染。

确认 ccache 是否真正介入编译过程

很多项目看似用了 ccache,实际编译命令绕过了它。关键看 g++clang++ 是否被 ccache 包装——不是靠 alias,而是靠 PATH 优先级或显式前缀。

  • 运行 which g++,输出应为类似 /usr/lib/ccache/g++(Debian/Ubuntu)或 /usr/local/bin/g++(若你软链了 ccache)
  • 检查编译日志中实际执行的命令:如果看到 ccache g++ -c .../usr/lib/ccache/g++ -c ...,说明生效;若只有裸 g++,ccache 没起作用
  • CMake 项目需显式设置:cmake -DCMAKE_CXX_COMPILER_LAUNCHER=ccache ...;仅设 CMAKE_CXX_COMPILERccache 是错的

避免因预处理差异导致缓存未命中

ccache 对输入源做哈希,任何影响预处理结果的变量(如绝对路径、时间宏、随机 salt)都会让哈希值变化,强制重编译。

  • 禁用 __DATE__ / __TIME__:在编译选项中加 -frecord-gcc-switches 无用,应统一用 -D__DATE__=\"redacted\" -D__TIME__=\"redacted\"
  • 避免绝对路径泄露:CMake 中用 set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 生成的 compile_commands.json 含绝对路径,但 ccache 不读它;真正要管的是 -I/path/to/include —— 若路径随 workspace 变化,考虑用符号链接固定位置
  • 禁止 -fdebug-prefix-map-frecord-gcc-switches 等引入非确定性信息的 flag(除非你明确需要调试信息且接受缓存失效)

合理配置缓存大小与位置

默认缓存位置(~/.ccache)可能空间不足或 I/O 慢;缓存满时 ccache 会自动清理,但策略偏保守,容易卡在 5GB 上限不动。

  • ccache -s 查看当前状态,重点关注 cache sizemax cache size
  • 增大上限:ccache -M 20G(注意是大写 M);清空缓存:ccache -C
  • 把缓存放 SSD 或内存盘:export CCACHE_DIR="/fast/ssd/ccache-$(whoami)",并确保该路径有读写权限
  • 启用压缩节省空间(小幅增加 CPU 开销):ccache -o c

    ompression=true -o compression_level=6

识别并修复“假命中”和静默失败

ccache 有时会返回旧缓存对象而不报错,尤其在头文件变更但未被依赖分析捕获时——这会导致奇怪的运行时错误。

  • 开启严格模式验证:设 export CCACHE_SLOPPINESS="pch_defines,time_macros,include_file_mtime,include_file_ctime,env_vars"(慎用,会降低命中率)
  • 临时禁用缓存调试问题:CCACHE_DISABLE=1 make,对比产物是否一致
  • 检查 ccache 日志:export CCACHE_LOGFILE="/tmp/ccache.log"; export CCACHE_DEBUG=1,然后复现一次编译,查看日志里是否有 hash mismatchpreprocessing failed
export CCACHE_DIR="$HOME/.ccache"
export CCACHE_BASEDIR="$PWD"
export CCACHE_SLOPPINESS="file_stat_matches"
alias g++='ccache g++'
alias clang++='ccache clang++'

最易被忽略的是 CCACHE_BASEDIR:它让 ccache 归一化源文件路径哈希,否则同一份代码在不同路径下编译,缓存完全不共享。还有就是 CMake 的 CMAKE_CXX_COMPILER_LAUNCHER 必须设对,不是所有构建系统都认 CC/CXX 环境变量里的 ccache。这些点没踩准,ccache 就只是个摆设。