如何加快g ++编译时间(使用大量模板时)

时间:2010-08-03 13:33:26

标签: c++ templates g++ compilation

这个问题可能有些奇怪,但我怎样才能加快g ++编译时间?我的C ++代码大量使用boost和模板。我已经尽可能多地从头文件中移动并使用-j选项,但是编译(和链接)仍需要很长时间。

是否有任何工具可以分析我的代码并指出编译器的瓶颈?或者可以以某种方式分析在我的代码上运行的编译器?这将是非常好的,因为有时我会有这样的印象,我花了太多时间盯着编译器控制台日志......

11 个答案:

答案 0 :(得分:48)

对我来说最有用的是:

  • 构建在RAM文件系统上。这在Linux上是微不足道的。您可能还希望在RAM文件系统上保留公共头文件(预编译或实际的.h文件)的副本。
  • Precompiled headers。我有一个(主要)库(例如Boost,Qt,stdlib)。
  • 尽可能声明而不是包含类。这样可以减少依赖关系,从而减少更改头文件时需要重新编译的文件数。
  • Parallelize make。这通常有助于逐个案例,但我在全球范围内-j3为make。但是,确保您的Makefile中的依赖关系图是正确的,否则您可能会遇到问题。
  • 如果您没有测试执行速度或代码大小,请使用-O0(并且您的计算机足够快,以至于您不必过多关注(可能很小的)性能损失。)
  • 每次保存时编译。有些人不喜欢这样,但是它允许你提前看到错误并且可以在后台完成,减少了你在完成写作并准备好测试时必须等待的时间。

答案 1 :(得分:17)

以下是我在您描述的非常类似的场景(boost,templates,gcc)中加快构建的所有工作

  • 构建在本地磁盘上而不是像NFS这样的网络文件系统
  • 升级到较新版本的gcc
  • 调查distcc
  • 更快的构建系统,尤其是更多内存

答案 2 :(得分:17)

我认为我们正在讨论分钟来编译文件,即预编译头文件或本地磁盘问题不是问题。

深度模板代码(boost等)的长编译时间通常源于gcc在模板实例化时的不友好的渐近行为,特别是当使用模板默认参数模拟可变参数模板时。

这是一个文档,它将减少的编译时间命名为可变参数模板的动机:

cpptruths有一篇关于gcc-4.5如何代表这一点以及它如何通过其可变参数模板表现出色的文章:

IIRC然后BOOST有办法限制伪变量的模板默认参数的生成,我认为'g ++ -DBOOST_MPL_LIMIT_LIST_SIZE = 10'应该有效(默认为20)

UPDATE:还有一个很好的线程,通常的技术可以加速在SO上进行编译,这可能很有用:

更新:这是关于编译模板时的性能问题,接受的答案也是建议使用gcc-4.5,同时也提到了clang作为正面例子:

答案 3 :(得分:9)

如果您正在进行大量重新编译,ccache可能有所帮助。它实际上并没有加快编译速度,但如果您因某些原因碰巧进行了无用的重新编译,它将为您提供缓存结果。它可能给人一种解决错误问题的印象,但有时重建规则非常复杂,以至于你在新版本中实际上最终会采用相同的编译步骤。

其他想法:如果您的代码使用clang进行编译,请改用它。它通常比gcc快。

答案 4 :(得分:3)

除了其他人添加的内容以及您已经在做的事情(并行化构建,编译器选项等)之外,考虑将模板隐藏在实现类中,通过接口访问。这意味着,而不是像这样的类:

// ClsWithNoTemplates.h file, included everywhere

class ClsWithTemplates
{
    ComplicatedTemplate<abc> member;
    // ...

public:
    void FunctionUsingYourMember();
};

你应该:

// ClsWithNoTemplates.h file:

class ClsWithTemplatesImplementation; // forward declaration
  // definition included in the ClsWithNoTemplates.cpp file
  // this class will have a ComplicatedTemplate<abc> member, but it is only 
  // included in your ClsWithNoTemplates definition file (that is only included once)


class ClsWithNoTemplates
{
     ClsWithTemplatesImplementation * impl; // no templates mentioned anywhere here
public:
    void FunctionUsingYourMember(); // call impl->FunctionUsingYourMember() internally
};

这会改变你的OOP设计,但这是好事:包括'ClsWithNoTemplates'的定义现在是 fast 而你只是(pre)编译'ClsWithNoTemplates'的定义一次。< / p>

另外,如果更改实现代码,则可能不需要重新定义包含ClsWithNoTemplates.h的任何代码。

此更改应显着增加您的部分编译时间,并且在ClsWithNoTemplates是从库文件导出的公共接口的情况下也会有所帮助:因为当您仅更改实现时,您的相关客户端不会更改文件代码根本不需要重新编译。

答案 5 :(得分:3)

尝试PIMPL技术,这个问题:What techniques can be used to speed up C++ compilation times?

它会阻止编译器在每次需要执行某些操作时跟踪头文件和实现链。

答案 6 :(得分:2)

如果有很多文件,只需要一个#include所有其他.cpp文件的.cpp文件,就可以加快编译速度。这当然要求您对宏更加小心,并且已经为每个文件定义了,因为它们现在对其他cpp文件可见。

如果文件很多,这可以减少编译时间。

答案 7 :(得分:1)

实例化较少的模板和内联函数。尽可能预编译,只需链接它,而不是从头开始编译所有内容。确保您使用的是最新版本的GCC。

然而,这是一个简单的事实,C ++是一种非常复杂的语言,编译需要相当长的时间。

答案 8 :(得分:1)

This paper描述了一种编译模板代码的方法,就像“传统的”非模板对象文件一样。保存编译和放大链接时间,每个模板实例化只需要一行代码开销。

答案 9 :(得分:0)

通常,编译中最昂贵的部分是(a)读取源文件( ALL )和(b)将编译器加载到每个源文件的内存中。

如果你有52个源(.cc)文件,每个文件#includes 47个#include(.h)文件,你将加载编译器52次,并且你将要翻阅2496个文件。根据文件中注释的密度,您可能花费大量时间吃无用的字符。 (在我看到的一个组织中,头文件在66%到90%的注释之间变化,只有10%-33%的文件是“有意义的”。为提高这些文件的可读性,可以做的唯一最好的事情就是剥离每个最后的评论,只留下代码。)

详细了解您的计划的实际组织方式。了解您是否可以合并源文件,并简化#include文件的层次结构。

几十年前,像IBM这样的公司理解这一点,并且会编写他们的编译器,以便编译器可以传递一个文件列表来编译,而不仅仅是一个文件,编译器只能加载一次。

答案 10 :(得分:0)