我有一些需要很多编译的cpp文件。它们包含一些基本的类/代码,带有一些模板,但没有什么可以证明编译时间大约几十秒。
我确实使用了几个外部库(boost / opencv)
这就是gcc所说的编译时间。如何找到可怕的编译时间的库/包含/函数调用?
Execution times (seconds)
phase setup : 0.00 ( 0%) usr 0.00 ( 0%) sys 0.01 ( 0%) wall 1445 kB ( 0%) ggc
phase parsing : 6.69 (46%) usr 1.61 (60%) sys 12.14 (47%) wall 488430 kB (66%) ggc
phase lang. deferred : 1.59 (11%) usr 0.36 (13%) sys 3.83 (15%) wall 92964 kB (13%) ggc
phase opt and generate : 6.25 (43%) usr 0.72 (27%) sys 10.09 (39%) wall 152799 kB (21%) ggc
|name lookup : 1.05 ( 7%) usr 0.28 (10%) sys 2.01 ( 8%) wall 52063 kB ( 7%) ggc
|overload resolution : 0.83 ( 6%) usr 0.18 ( 7%) sys 1.48 ( 6%) wall 42377 kB ( 6%) ggc
...
Profiling the C++ compilation process涉及识别慢速文件,但我需要更细粒度的信息才能找到罪魁祸首
(其他文件/项目以毫秒/秒编译,所以它不是计算机资源的问题。我使用gcc 4.9.1)
答案 0 :(得分:17)
基本上有两件事导致编译时间过长:包含太多内容和模板太多。
当你包含太多标题并且这些标题包含了太多自己的标题时,它只意味着编译器有很多工作要做,以加载所有这些文件,它将花费大量的时间关于它必须对所有代码执行的处理过程,无论它是否实际使用,如预处理,词法分析,AST构建等等。当代码分布在大量的代码上时,这可能特别成问题小标题,因为性能非常受I / O限制(大量时间浪费只是从硬盘中读取和读取文件)。不幸的是,Boost库往往以这种方式结构化。
以下是解决此问题的几种方法或工具:
-E
或-E -P
选项,或者只是使用GCC的C预处理程序{{1直接)。您可以获取源文件并注释掉不同的include语句或include语句组,并转储预处理的源以查看这些不同标头所引入的代码总量(并且可以使用行计数命令,如{{1} })。这可以帮助您识别要处理的大量代码行,哪些标题是最严重的违规者。然后,您可以看到您可以做些什么来避免它们或以某种方式缓解问题。第二个问题是模板实例化。在GCC的时间报告中,应该在模板实例化阶段报告某个时间值。如果该数字很高,那么只要代码中涉及大量模板元编程,就会出现这种情况,那么您将需要处理该问题。有很多原因可以解释为什么一些模板繁重的代码编译速度很慢,包括深度递归的实例化模式,过于花哨的Sfinae技巧,滥用类型特征和概念检查,以及旧的时尚过度设计的通用代码。但是也有一些简单的技巧可以修复很多问题,例如使用未命名的命名空间(以避免浪费所有时间浪费生成实例化的符号,而这些符号实际上不需要在翻译单元之外可见)和专门化类型特征或概念检查模板(基本上是#34;短路"很多花哨的元编程进入它们)。模板实例化的另一个潜在解决方案是使用" extern templates" (来自C ++ 11)控制应该实例化特定模板实例化的位置(例如,在单独的cpp文件中),并避免在它使用的任何地方重新实例化。
以下是一些帮助您识别瓶颈的方法或工具:
但即使有这些技巧,识别模板实例化瓶颈比实际解决它们更容易。所以,祝你好运。
答案 1 :(得分:1)
如果没有关于如何组织和构建源文件的信息,这是无法完全回答的,所以只是一些一般性的观察。
#include
d的头文件,无论是否需要,都会增加编译时间。我看到一个案例,其中一个团队成员写了"globals.h"
#include
d所有内容,#include
d到处都是 - 并且构建时间(在一个大型项目中)增加了一个大小。这是双重打击 - 每个源文件的编译时间都会增加,并且会乘以直接或间接#include
该标头的源文件数。如果启用“预编译头”等功能会导致第二次和后续版本的构建时间加快,这可能是一个贡献者。 (您可以将预编译的头文件视为此解决方案,但请记住使用它们还有其他权衡因素。)除此之外,找到问题的技术取决于构建过程的结构。
如果您正在使用分别编译源文件的makefile(或其他方法),则使用某种方式为单个编译和链接命令计时。请记住,它可能是主导的链接时间。
如果您正在使用单个编译命令(例如,在一个命令中对多个文件调用gcc),则将其分解为每个源文件的各个命令。
一旦你隔离了哪个源文件(如果有的话)是罪犯,那么有选择地从中删除一些部分以找出其中的哪个代码是问题。正如Yakk在评论中所说,使用“二分搜索”来消除文件中的功能。我建议先删除整个函数(缩小到有问题的函数),然后在违规函数中使用相同的技术。
它有助于构建代码,因此每个文件的函数数量相当小。这减少了重建大文件的需要,以便对一个功能进行微小更改,并有助于在将来更轻松地隔离这些问题。