使用GCC预编译头文件

105

如何在GCC中启用预编译头文件?

我尝试了很多次,但都没有成功,而且我也没有看到很好的设置示例。我曾在Cygwin GCC 3.4.4和在Ubuntu上使用4.0版本进行过尝试。


我尝试了一下,发现预编译头文件对我的C源代码非常有效,因为它是由编译器生成而不是用户编写的。Sun Studio和特别是Visual Studio显著提高了构建时间。在gcc上,没有使用预编译头文件甚至会更糟。这是在3.4版本中测试的,尚未在4.x版本中进行过测试,但速度和gcc是互斥的。 - Lothar
@Lothar,那段代码是什么?我发现在一些使用了大量模板的代码上,g++比最近的Visual Studio编译器快了约10倍。 - the swine
我的C++代码中没有使用模板。它只是C + 异常处理 + 不错的C++扩展。即使在这个问题6年后,VS2010仍然快了一个数量级。但与此同时,我有16个核心,所以我可以接受它。 - Lothar
7个回答

64
我确实取得了成功。首先,我使用了以下代码:
#include <boost/xpressive/xpressive.hpp>
#include <iostream>

using namespace std;
using namespace boost::xpressive;

// A simple regular expression test
int main()
{
    std::string hello("Hello, World!");

    sregex rex = sregex::compile( "(\\w+) (\\w+)!" );
    smatch what;

    if( regex_match( hello, what, rex ) )
    {
        std::cout << what[0] << '\n'; // Whole match
        std::cout << what[1] << '\n'; // First capture
        std::cout << what[2] << '\n'; // Second capture
    }
    return 0;
}

这只是一个来自 Boost Xpressive 的“Hello, World!”程序。首先,我使用 GCC 编译器的 -H 选项进行编译。它显示了一个使用的头文件巨大的列表。然后,我查看了我的 IDE(Code::Blocks)生成的编译标志,并看到类似于这样的内容:
g++ -Wall -fexceptions  -g  -c main.cpp -o obj/Debug/main.o

所以我编写了一个命令,使用完全相同的标志来编译 Xpressive.hpp 文件:
sudo g++ -Wall -fexceptions  -g /usr/local/include/boost/xpressive/xpressive.hpp

我使用 -H 重新编译了原始代码,得到了以下输出:
g++ -Wall -fexceptions -H  -g     -c main.cpp -o obj/Debug/main.o

! /usr/local/include/boost/xpressive/xpressive.hpp.gch
main.cpp
. /usr/include/c++/4.4/iostream
.. /usr/include/c++/4.4/x86_64-linux-gnu/bits/c++config.h
.. /usr/include/c++/4.4/ostream
.. /usr/include/c++/4.4/istream
main.cpp

“!”表示编译器能够使用预编译头文件,“x”表示无法使用。使用适当的编译器标志至关重要。我去掉了“-H”并进行了一些速度测试。与未使用预编译头文件相比,预编译头文件的性能从14秒提高到11秒。不错,但不是很好。
注意:这里是示例。我无法在帖子中使其工作。
顺便说一下:我正在使用以下的

24
加上“-Winvalid-pch”选项可以帮助你调试预编译头文件(PCH)的使用过程中出现的问题及原因。 - lefticus
“不错但不是很好”,预编译头文件在你有许多互相链接的头文件时非常有用,因此它们可以减少在使用大型库或许多库的非常大项目上的编译时间。 - jokoon
4
“不太糟糕但也不算很好”: 使用gcc 4.4.7版本,共有136个.cpp文件,总大小为35.5Mb;148个.h文件,总大小为5.5Mb;而.gch文件的大小为48Mb。在Debug编译模式下所需时间为2分20秒(与未使用预编译头文件相比较少了6秒),而在优化编译模式下所需时间为4分30秒(与未使用预编译头文件相比缩短了1分3秒)。尽管效果通常会在Debug构建中更为显著,但实际上只有在优化编译模式下使用预编译才会提高编译速度……不确定具体原因。而在Windows系统中,预编译的效果则更加显著! - Andreas Vergison
1
继续上文,相应的pch/non-pch输出文件具有完全相同的字节大小,这很好。上述时间似乎在重复构建时会有所变化,例如-O2非pch在3'45"和5'33"之间变化,因此这并不是精确的科学,可能是由于在VMware中运行。无论如何,在我的情况下,gcc pch看起来并没有什么好处。将其与Windows VS2012上的相同代码库进行比较(x64,单线程编译):debug 46" pch,2'50" non-pch,release 2'13" pch,5'02" non-pch。当然,启用多处理器时速度更快... - Andreas Vergison
@AndreasVergison - 你尝试过使用“-Winvalid-pch”来确保预编译头文件被正确使用吗?我们注意到在调试构建中使用pch会有很大的改进,所以我想知道你的设置是否存在问题。 - Josh Kelley
如果有人在Mac上使用Clang,相同的标志也适用。非常感谢这个答案,它对我帮助很大。 - Sri Harsha Chilakapati

58

首先,参见此处的文档

您可以像编译其他文件一样编译头文件,但需要将输出放在一个后缀名为.gch的文件中。

例如,如果您预编译了stdafx.h,则会生成一个预编译头文件,每次包含stdafx.h时都会自动搜索名为stdafx.h.gch的文件。

示例:

stdafx.h:

#include <string>
#include <stdio.h>

a.cpp:

#include "stdafx.h"
int main(int argc, char**argv)
{
  std::string s = "Hi";
  return 0;
}

然后按照以下方式进行编译:

> g++ -c stdafx.h -o stdafx.h.gch
> g++ a.cpp
> ./a.out

即使在第一步之后删除stdafx.h,您的编译也将正常工作。


10

-x指定C++预编译头文件的选项应该是-x c++-header而不是-x c++。以下是使用PCH的示例。

pch.h

// Put your common include files here: Boost, STL as well as your project's headers.

main.cpp:

#include "pch.h"
// Use the PCH here.

按照以下方式生成PCH:

$ g++ -x c++-header -o pch.h.gch -c pch.h

pch.h.gch 文件必须与 pch.h 文件在同一个目录下才能使用,因此请确认你在 pch.h 文件所在的目录中执行上述命令。


3
应该是 -c pch.h 而不是 -c pch.cpp,对吗? - M.M

9

使用与为源文件调用GCC相同的方式来调用它,只不过需要加上头文件。

例如:

g++ $(CPPFLAGS) test.h

这将生成一个名为test.h.gch的文件。

每次GCC搜索test.h时,它首先查找test.h.gch,如果找到了它,就自动使用它。

更多信息可在GCC预编译头文件中找到。


我正在使用gcc 3.4,执行命令"g++ stdafx.h"无法编译,会出现错误 "g++: compilation of header file requested",但是这个命令可以编译:"g++ -c -x c++ stdafx.h -o stdafx.h.pch",不确定是否符合我的需求。 - stefanB

8
我曾经成功地在gcc下使用预编译头文件,但我记得当时也遇到了问题。需要注意的是,如果不满足特定条件,gcc将忽略文件(header.h.gch或类似文件)。这些条件列表可以在gcc预编译头文件文档页面中找到。
通常最安全的做法是让您的构建系统将.gch文件作为第一步进行编译,使用与源代码相同的命令行选项和可执行文件。这可以确保文件是最新的,并且没有细微差别。
最好先用一个人为的例子使其正常工作,以消除问题是特定于项目源代码的可能性。

3

请确保-include your_header.h

这是我如何预编译并使用bits/stdc++.h集合的方式。

代码

#include <bits/stdc++.h>

然后我通过使用-H编译我的文件并查看输出来定位库。

g++ sol.cpp -H -O3 -pthread -lm -std=c++14 -o executable

这是我看到的地方

. /usr/include/x86_64-linux-gnu/c++/7/bits/stdc++.h

我在当前目录下创建了一个名为bits的新文件夹,并从中复制了stdc++.h

然后我运行了

g++ bits/stdc++.h -O3 -std=c++14  -pthread

生成bits/stdc++.gch的方式如下:

通常情况下,我会通过以下方式编译我的代码:

g++ sol.cpp -O3 -pthread -lm -std=c++14 -o executable

但我必须对此进行修改

g++ sol.cpp -include bits/stdc++.h -O3 -pthread -lm -std=c++14 -o executable

当使用 -include bits/stdc++.h 时,它仅解析为 .gch 文件,而不是 .h 文件,这对我来说很关键。还要记住的是,您必须将 *.h 头文件编译成几乎与编译 *.cpp 相同的参数。当我没有包含 -O3-pthread 时,它会忽略 *.gch 预编译头文件。

为了检查一切是否正确,您可以通过比较以下结果来测量时间差异:

time g++ sol.cpp ...

或运行
g++ sol.cpp -H -O3 -pthread -lm -std=c++14 -o executable

再次查找头文件路径,如果你现在在库路径之前看到!,例如:

! ./bits/stdc++.h.gch
....

非常感谢您的回答,我已经为此苦恼了两天了 :) - LachoTomov

0
关于文件扩展名的微妙提示让我犯了错,因为我没有仔细注意:预编译文件的完整名称会添加 .gch 扩展名,而不是替换 .h。如果你弄错了,编译器将无法找到它并且默默地不起作用。
precomp.h => precomp.h.gch
而不是:
precomp.h => precomp.gch
使用 GCC 的 -H 选项来检查是否找到/使用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接