要添加到预编译头文件的内容

Coo*_*kie 6 c++ compilation include precompiled-headers

我是预编译头文件的新手,我只是想知道要包含什么.我们的项目有大约200个源文件.

那么,我真的包含每个第三方库吗?

如果我在三个源文件中使用地图,我会添加它吗?如果我使用它,我会添加它吗?我是否需要删除旧的直接包含或ifdef和pragma一旦指令仍然有效?

你有没有添加任何第三方图书馆?

那么预编译的头部不会变得庞大吗?

因为,即使在预编译的形式中,也不会有突然出现所有这些标题的开销吗?

编辑:

我找到了一些关于clang的信息:

在以下情况下,预编译的头实现可提高性能:

  • 加载PCH文件比重新解析存储在PCH文件中的标头包快得多.因此,预编译的头部设计试图最小化读取PCH文件的成本.理想情况下,此成本不应随预编译头文件的大小而变化.
  • 最初生成PCH文件的成本不是很大,以至于它不需要首先解析捆绑的头文件,因此它可以抵消每个源文件的性能提升.这在多核系统上尤为重要,因为当所有编译都要求PCH文件是最新的时,PCH文件生成会对构建进行序列化.

Clang的预编译头文件采用紧凑的磁盘表示,可最大限度地减少PCH创建时间和初始加载PCH文件所需的时间.PCH文件本身包含Clang的抽象语法树和支持数据结构的序列化表示,使用与LLVM的bitcode文件格式相同的压缩比特流存储.

Clang的预编译头文件是从磁盘"懒洋洋地"加载的.当最初加载PCH文件时,Clang只从PCH文件中读取少量数据,以确定存储某些重要数据结构的位置.在此初始加载中读取的数据量与PCH文件的大小无关,因此较大的PCH文件不会导致较长的PCH加载时间.PCH文件中的实际标题数据 - 宏,函数,变量,类型等 - 仅在从用户代码引用时才加载,此时只对该实体(及其依赖的实体)进行反序列化来自PCH文件.使用这种方法,使用预编译头部用于转换单元的成本与从头部实际使用的代码量成比例,而不是与头部本身的大小成比例.

对我来说,这似乎表明至少是clang:

  • 已经注意使预编译头的加载时间与大小无关.
  • 预编译头的使用时间与预编译头大小无关,并且与使用的信息量成比例
  • 与目前给出的答案相反,这似乎表明,即使只包含一个外部文件(比如说<map>),也值得将它包含在预编译的头文件中(仍然会加快对该源文件的重新编译)

必须有某种地图来绘制所有信息.这张地图可能会变大,但也许这不重要?不确定我是否正确,或者它是否适用于所有编译器......

小智 7

对此没有100%的确切答案,因为它取决于您的项目.最好的办法是亲自尝试,看看会发生什么.

然而,

"那么,我真的要包括每个第三方图书馆吗?"

不,基本上你包括标题,其中:

  • 经常被您的来源使用.然而,"经常"并没有完全定义,但是我们说它被超过10%的源文件使用了.(然而,我随机选择了数字.也许它应该更大.)
  • 大部分时间都没有改变(因为单个标题的更改意味着您需要重新编译所有源代码).第三方库预计不会被更改,因此它们是最佳候选者,但如果您确定它们很少会被更改或在特殊情况下,您也可以使用自己项目中的标题.

但不要"只"包括库的所有标题.包括你正在使用的那些.

"如果我在三个源文件中使用地图,我会添加吗?"

往上看.对此没有明确的答案,但我个人认为三个源文件太少了.

"如果我使用它,我会添加它吗?"

(我理解这个问题,因为它会"如果我在单一来源中使用头文件并将其添加到预编译的头文件中怎么办?")

什么都不会破坏你的应用程序.但它会:

  • 预编译头的编译时间更长.
  • 预编译头文件更大.
  • 编译源文件较慢.

在单个标题的情况下,如果标题具有平均大小,则它是完全无关紧要的.但是,如果添加数百个此类标头,则会降低整个编译速度.

"一旦指令仍然有效,我是否需要删除旧的直接包含或ifdef和pragma?"

你可能做这样的事情,但我强烈建议不要这样做.但是,您无需这样做.

您可以想象,预编译的标题只是在所有源的开头包含标题.例:

precompiled.h

#include <iostream>
#include <string>
Run Code Online (Sandbox Code Playgroud)

MyClass.cpp

#include "MyClass.h"

MyClass::MyClass()
{
// etc.
Run Code Online (Sandbox Code Playgroud)

现在假设你启用了预编译的头文件.对于源文件,它就像你写的一样:

#include "precompiled.h"
#include "MyClass.h"

MyClass::MyClass()
{
// etc. 
Run Code Online (Sandbox Code Playgroud)

你能这样做吗?是的你可以!预编译的标题就像那样(但它更快),这意味着:

  • 是的,保留了宏.在所有源文件中定义了预编译头文件中定义的内容,这意味着:
    • 警卫正常工作.
    • 如果存在检测OS的库,则仍然定义并可用所有宏.
    • 如果定义了其他宏(例如MIN(尽管现在建议使用std :: min)),您仍然可以正常使用它.
  • 我不知道pragma,但我相信它也能正常工作.

关于删除来源:如上所述,我非常反对.原因很简单:如果您将来需要关闭预编译的头文件怎么办?(事实上​​,我个人不时会关闭预编译的标题,看看我的代码是否仍然编译.我个人的理由是,如果你发布代码,一些用户将不会使用你的项目/ make文件,但他们会创建自己的项目(例如,如果他们使用不同的IDE,如Code :: Blocks或QtCreator),所以我尝试以这样的方式制作我的项目,你只需要添加源文件,配置正确的包含路径,链接正确的库,它应该编译.)

"你有没有添加任何第三方图书馆?"

我想不出任何......

另一方面,我可以想到一些最好添加的恕我直言(如果你使用它们) - 例如提升.它大部分时间都使用模板 - 根据我的个人经验,模板最大程度地减慢了编译速度,因为您需要不包括onlu声明,还需要包含定义.恕我直言,这是C++模板的最大弱点.

"然后,预编译的头部不会变得庞大吗?"

他们能.这就是为什么你需要找到最好的头文件子集(而不是盲目地包括所有内容)来获得最佳结果.我的大约50MB,但仍然加快编译速度.(整个分钟,因为我使用模板很多.)

"就像在,所有这些标题包含在所有地方,即使是预编译形式,也不存在开销吗?"

如果使用预编译头,则准备一些头文件并将其包含在所有源文件中.这意味着,从单个源文件的角度来看,您包含了源文件不需要的一些头文件.是开销.但是,包含预编译头文件要快得多,因此如果包含一些不需要的头文件,它仍然会更快.但是,当你跨越某个限制时(假设超过90%的来源,超过90%包含标题是不需要的),当使用预编译的标题时,开始减慢编译速度.这就是为什么你需要包含主要使用的标题,并避免包含仅包含在少数源文件中(或根本不使用)的标题.

通常,使用预编译的头文件会增加磁盘上所需的空间(在这些时候,绝对不显着)和RAM中所需的空间(同样,这些时间并不重要).这是"以内存为代价获得更高速度"的完美范例.


最后的建议很简单:亲自尝试一下.检查添加标题时会发生什么,当您觉得编译速度很慢时,请检查是否包含了大部分未使用的内容.


MxN*_*xNx 2

您可能知道,编译 C/C++ 源代码是一项耗时的任务,其原因之一是编译器需要编译您直接或间接包含在源代码中的每一块代码,在大多数情况下,冗余,因为大多数包含的文件都是不会随时间变化的库。为了缓解这个问题,引入了预编译头的概念。通过预编译头,我们可以告诉编译器一组包含文件不太可能随着时间的推移而改变,这样编译器就可以通过编译一次指定的文件并保存结果来优化编译过程。然后,每当编译器需要编译项目时,它都会跳过指定源的编译并重用这些保存的编译文件。

因此,最好将不会频繁更改的文件包含在预编译头中。当然,您可以在其中添加经常更改的代码,但这将忽略使用预编译头的全部意义。

顺便说一句,不用担心预编译头会变得庞大。这个概念主要是为了减少通常有大量第三方库的大型项目的编译时间。在这些情况下,这些文件通常应该而且将会变得很大。

另请参阅维基百科有关预编译标头的条目