识别编译速度慢的函数

Sam*_*Sam 11 c++ performance gcc boost compilation

我有一些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
...
Run Code Online (Sandbox Code Playgroud)

分析C++编译过程涉及识别慢速文件,但我需要更细粒度的信息才能找到罪魁祸首

(其他文件/项目以毫秒/秒编译,所以这不是计算机资源的问题.我使用gcc 4.9.1)

Mik*_*son 17

基本上有两件事会导致编译时间过长:包含太多的模板和太多的模板.

当你包含太多标题并且这些标题包含了太多自己的标题时,这只意味着编译器有很多工作要做,以加载所有这些文件,它将花费大量的时间来处理传递它必须对所有代码执行,无论它是否实际使用,如预处理,词法分析,AST构建等.当代码分布在大量小标题上时,这可能特别成问题,因为性能非常受I/O限制(浪费了大量时间只是从硬盘中读取和读取文件).不幸的是,Boost库往往采用这种方式构建.

以下是解决此问题的几种方法或工具:

  • 您可以使用" 包含使用对象 "工具.这是一个基于Clang的分析工具,它基本上会查看您在代码中实际使用的内容,以及这些内容来自哪些标题,然后使用前向声明来报告您可以通过删除某些不必要的包含而进行的任何潜在优化或者用更细粒度的标题替换更广泛的"一体化"标题.
  • 大多数编译器都有转储预处理源的选项(在GCC/Clang,它-E-E -P选项上,或者直接使用GCC的C预处理程序cpp).您可以获取源文件并注释掉不同的include语句或include语句组,并转储预处理的源以查看这些不同标头所引入的代码总量(并且可能使用行计数命令等$ g++ -E -P my_source.cpp | wc -l).这可以帮助您识别要处理的大量代码行,哪些标题是最严重的违规者.然后,您可以看到您可以做些什么来避免它们或以某种方式缓解问题.
  • 您还可以使用预编译的标头.这是大多数编译器支持的功能,您可以使用它来指定某些标头(特别是包含在内的"一体化"标头)以进行预编译,以避免为包含它们的每个源文件重新解析它们.
  • 如果您的操作系统支持它,您可以使用ram磁盘作为代码和外部库的标头.这基本上占用了你的部分RAM内存,使它看起来像普通的硬盘/文件系统.这可以通过减少I/O延迟来显着减少编译时间,因为所有头文件和源文件都是从RAM内存而不是实际硬盘中读取的.

第二个问题是模板实例化.在GCC的时间报告中,应该在模板实例化阶段报告某个时间值.如果该数字很高,那么只要代码中涉及大量模板元编程,就会出现这种情况,那么您将需要处理该问题.有很多原因可以解释为什么一些模板繁重的代码编译速度很慢,包括深度递归的实例化模式,过于花哨的Sfinae技巧,滥用类型特征和概念检查,以及旧的时尚过度设计的通用代码.但也有一些简单的技巧可以解决很多问题,例如使用未命名的命名空间(以避免浪费所有时间浪费生成实际上不需要在翻译单元外可见的实例化的符号)和专门化类型特征或概念检查模板(基本上"短路"进入它们的许多花哨的元编程).模板实例化的另一个潜在解决方案是使用" extern模板 "(来自C++ 11)来控制应该实例化特定模板实例化的位置(例如,在单独的cpp文件中)并避免在其使用的任何地方重新实例化它.

以下是一些帮助您识别瓶颈的方法或工具:

  • 您可以使用" Templight "分析工具(及其辅助" Templight-tools "来处理跟踪).这又是一个基于Clang的工具,可以用作Clang编译器的替代品(该工具实际上是一个仪表化的完整编译器),它将生成编译期间发生的所有模板实例化的完整配置文件. ,包括在每个上花费的时间(以及可选的内存消耗估计,尽管这会影响时序值).稍后可以将跟踪转换为Callgrind格式并在KCacheGrind中可视化,只需在templight-tools页面上阅读该描述.这基本上可以像典型的运行时分析器一样使用,但用于在编译模板大量代码时分析时间和内存消耗.
  • 寻找最严重违法者的更基本方法是创建测试源文件,以实例化您怀疑负责长编译时间的特定模板.然后,您编译这些文件,计时,并尝试按照自己的方式(可能采用"二进制搜索"方式)对付最严重的违规者.

但即使有这些技巧,识别模板实例化瓶颈比实际解决它们更容易.所以,祝你好运.