C++怎么提高编译速度 C++预编译头文件设置【高级】

3次阅读

预编译头(pch)仅在80%以上.cpp文件共用稳定大头文件时才有效;否则增加构建复杂度。启用需严格匹配编译参数、平台和标准,且pch内容须精简精准,避免宏污染与跨标准复用。

C++怎么提高编译速度 C++预编译头文件设置【高级】

预编译头(PCH)到底该不该开

多数项目在启用预编译头后,首次编译变慢、后续编译才快——但如果你的代码频繁修改 stdafx.hpch.h,它反而拖累整体迭代速度。是否启用,取决于头文件稳定性和团队协作模式。

判断依据很简单:项目里 80% 以上的 .cpp 文件是否都包含同一组不变的大头文件? 比如 <vector></vector><String></string><memory></memory><boost></boost> 这类不常改又很重的头。如果是,PCH 才真有用;否则只是徒增构建配置复杂度。

  • windows + MSVC:默认支持 pch.h/pch.cpp,但必须确保所有源文件顶部第一行是 #include "pch.h"(不能有空行、注释或宏)
  • linux/macos + Clang/GCC:需手动用 -x c++-header 生成 pch.gchpch.pch,且路径、语言标准(-std=c++17)必须和实际编译完全一致,否则静默失效
  • CI 环境中容易漏掉 PCH 缓存清理,导致旧 PCH 被复用却链接失败——错误现象通常是 undefined reference to 'std::basic_string<...>::...' </...> 这类符号缺失,而非编译报错

clang++ 和 g++ 的 PCH 生成命令差异

Clang 和 GCC 对预编译头的处理逻辑不同,直接套用对方命令会失败。关键不是“能不能生成”,而是“生成后能否被后续编译真正识别并加载”。

common.h 为例:

立即学习C++免费学习笔记(深入)”;

clang++ -x c++-header -std=c++17 -I./include common.h -o common.h.pch

对应使用时加:

clang++ -include common.h -Xclang -include-pch -Xclang common.h.pch main.cpp

而 GCC 必须用 .gch 后缀,且不能显式指定加载:

g++ -x c++-header -std=c++17 -I./include common.h -o common.h.gch

然后编译时只需保证路径对、#include "common.h" 在最前,GCC 就自动匹配同目录下的 common.h.gch。多一个字母写成 common.h.pch,GCC 完全无视。

  • Clang 的 -include-pch 是强制加载,哪怕 #include 行不存在也生效;GCC 完全依赖文件名和位置匹配
  • 两者都不支持跨语言标准复用:用 -std=c++20 生成的 PCH,不能用于 -std=c++17 编译的源文件
  • 若头文件里有 #ifdef __linux__ 这类平台宏,GCC 生成的 PCH 在 macOS 上必然失效——Clang 同理

为什么加了 PCH 反而更慢

常见原因是 PCH 本身成了瓶颈:体积过大、包含无关内容、或与实际编译参数不一致。预编译头不是“越多越好”,而是“越准越稳”。

典型症状:make -j4 下 CPU 占用低、单个 .cpp 编译耗时反而比关掉 PCH 还长。

  • 检查 PCH 内容:删掉所有只在个别文件里用的头,比如 <opencv2></opencv2>"network_client.h" —— 它们应该留在具体源文件里
  • 避免在 PCH 中定义宏,尤其是影响模板实例化的(如 #define _HAS_AUTO_PTR_ETC 0),会导致 PCH 和源文件看到的类型不一致
  • MSVC 下若开启 /Zi(生成调试信息),PCH 文件可能超 200MB,磁盘 I/O 成为瓶颈;改用 /Z7 并确保 SSD 存储
  • Clang 的 PCH 加载过程会做 Token 校验,若 PCH 是用 -O2 生成、而源文件用 -O0 编译,部分优化相关宏行为不一致,也会退化为普通头处理

CMake 中正确集成 PCH 的要点

CMake 对 PCH 的支持是“半托管”:它能帮你生成和传递参数,但不会校验一致性。很多项目在升级 CMake 版本后 PCH 失效,就是因为 target_precompile_headers() 的行为变了。

CMake 3.16+ 推荐写法:

target_precompile_headers(mylib private   "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include/pch.h>" )

注意:PRIVATE 表示只影响本 target 的编译,不传递给依赖者;若写成 public,下游 target 可能意外继承,引发冲突。

  • 不要在 add_compile_options() 里硬塞 -include,这会绕过 CMake 的 PCH 管理逻辑,导致 Ninja/Makefile 生成不一致
  • 跨平台项目慎用 target_precompile_headers():CMake 在 MinGW 下默认禁用 PCH,即使写了也不会报错,而是静默忽略
  • 如果项目含大量 header-only 库(如 Eigen、fmt),它们不该进 PCH——这些库本身设计为快速 include,预编译反而增加冗余解析

最麻烦的从来不是怎么配,而是怎么让所有人改完头文件后记得更新 PCH 列表,以及怎么在 git diff 里一眼看出某次提交是否破坏了 PCH 的稳定性。

text=ZqhQzanResources