预编译头文件(Precompiled Header,PCH)是一种可以用来提高编译速度的技术。它的原理是先将一些常用的头文件预处理,生成一个二进制文件(通常是.pch文件),然后在编译其他源文件时,直接引用这个二进制文件,避免了重复的预处理过程,从而提高了编译速度。
scope
PCH 技术不是万能的,它只能加快编译过程中头文件的处理。PCH 并不是所有情况下都能提高编译速度,只有在项目中包含大量的头文件且重复使用时,才能显著地提高编译速度。包括:
- 标准库头文件
- 第三方库头文件
- 宏定义
- 预定义的类型
这些文件通常不会在项目中频繁改变,因此预编译它们可以避免重复编译,如果这些代码被修改,则需要重新编译生成预编译头文件。
编译原理
为了更好地理解编译优化方案,在介绍优化方案之前,我们先简单介绍一下编译原理,通常我们在进行C++开发时,编译的过程主要包含下面四个步骤:
-
- 预处理(Preprocessing):主要用于处理#开头的代码行,头文件展开,条件编译展开,删除注释。文件以.i和.ii结尾。
- 编译(Compilation):使用预处理的输出结果作为输入,生成文本格式的平台相关的汇编代码(assembly code)。文件以.s结尾。
- 汇编(Assemble):将上一步的汇编代码转换成二进制的机器码,称为object code。产生的文件叫做目标文件,是二进制格式,文件以.o或.obj结尾。
- 链接(Linking):链接过程将多个目标文以及所需的库文件(.so等)链接成最终的可执行文件(executable file)
步骤 | 命令 | 等价命令 | 输出文件 |
---|---|---|---|
预处理 | cpp | gcc -E | .i, .ii |
编译 | cc1, cc1plus | gcc -S | .s |
汇编 | as | gcc -c | .o, .obj |
链接 | ld | gcc | 可执行文件 |
C/C++编译器只编译源文件(.c/.cc/.cxx/.cpp),每个源文件都编译成为一个.o/.obj文件,最后由链接器链object文件成为可执行文件。
编译器在编译源文件之前,会将宏定义、头文件等加载到源文件.cpp文件。即#include某个文件,相当于将这个文件的代码全部拷贝过来替换了#include语句。说白点就是把头文件里内容直接“粘帖”到相应的#include 语句处。
因此变量的定义、函数的定义不要写到头文件中。因为头文件很可能要被多个cpp引用。当连接的时候可能会出现重复定义的情况。
C++编译特点
-
- 每个源文件独立编译
C/C++的编译系统和其他高级语言存在很大的差异,其他高级语言中,编译单元是整个Module,即Module下所有源码,会在同一个编译任务中执行。而在C/C++中,编译单元是以文件为单位(C++20有了Module)。每个.c/.cc/.cxx/.cpp源文件是一个独立的编译单元,导致编译优化时只能基于本文件内容进行优化,很难跨编译单元提供代码优化。
2. 每个编译单元,都需要独立解析所有包含的头文件
如果N个源文件引用到了同一个头文件,则这个头文件需要解析N次,对于动辄几千上万行的头文件来说,简直不敢想象。
因此,PCH技术就是将这些通常不会在项目中频繁改变的头文件预编译成二进制文件,然后编译其他源文件时,直接使用这个二进制文件,而不需要再次解析和处理这些头文件,这样可以节省编译时间和内存消耗,特别是当项目中有很多头文件时,在编译源代码时将其加载到内存中,以加快编译速度。
PCH 技术的步骤
创建预编译头文件:开发者需要先创建一个预编译头文件,该文件通常包含项目中的常用头文件。这个预编译头文件可以是一个单独的文件,也可以是一个包含多个头文件的文件。在 Visual Studio 中,可以使用 “.pch” 或 “.pchx” 扩展名来指定预编译头文件。
使用预编译头文件:在编译其他源文件时,需要使用预编译头文件来加快编译速度。在源文件的开头,使用 #include 指令来包含预编译头文件,例如再windows上:#include "stdafx.h" // 预编译头文件名
管理PCH文件
PCH文件包含一些常用的头文件和库文件的声明和定义,以便在编译其他源代码文件时提高编译速度。如果你想避免编译器生成compiler_depend.ts文件,可以使用以下方法:
-
- 禁用PCH文件
禁用PCH文件可以让编译器不生成compiler_depend.ts文件。具体来说,你可以使用编译器选项-Winvalid-pch或-x c++来禁用PCH文件。例如,在使用g++编译器时,你可以使用以下命令:
g++ -c -Winvalid-pch -o main.o main.cpp
这将禁用PCH文件,并且编译器不会生成compiler_depend.ts文件。 - 指定PCH文件名
如果你需要使用PCH文件,但是不想使用默认的文件名compiler_depend.ts,你可以使用编译器选项-Winvalid-pch或-x c++来指定PCH文件名。例如,在使用g++编译器时,你可以使用以下命令:
g++ -c -x c++-header -o mypch.pch myheader.h
这将生成名为mypch.pch的PCH文件,而不是默认的compiler_depend.ts文件。
需要注意的是,禁用PCH文件可能会影响编译速度,因为每次编译都需要重新编译所有头文件。如果你需要频繁地编译大量的源代码文件,启用PCH文件可以显著提高编译速度。
- 禁用PCH文件