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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3396958/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 12:45:50  来源:igfitidea点击:

How to speed up g++ compile time (when using a lot of templates)

c++templatesg++compilation

提问by Danvil

This question is perhaps somehow odd, but how can I speed up g++ compile time? My C++ code heavily uses boost and templates. I already moved as much as possible out of the headers files and use the -j option, but still it takes quite a while to compile (and link).

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

Are there any tools out there which analyse my code and point out bottle-necks for the compiler? Or can one somehow profile the compiler running on my code? This would be really nice, because sometimes I have the impression, that I spent too much time staring at the compiler console log ...

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

采纳答案by strager

What has been most useful for me:

对我最有用的是:

  • Build on a RAM filesystem. This is trivial on Linux. You may want to keep a copy of common header files (precompiled or the actual .h files) on the RAM filesystem as well.
  • Precompiled headers. I have one per (major) library (e.g. Boost, Qt, stdlib).
  • Declare instead of include classes where possible. This reduces dependencies, thus reduces the number of files which need to be recompiled when you change a header file.
  • Parallelize make. This usually helps on a case-by-case basis, but I have -j3globally for make. Make sure your dependency graphs are correct in your Makefile, though, or you may have problems.
  • Use -O0if you're not testing execution speed or code size (and your computer is fast enough for you not to care much about the (probably small) performance hit).
  • Compile each time you save. Some people don't like this, but it allows you to see errors early and can be done in the background, reducing the time you have to wait when you're done writing and ready to test.
  • 在 RAM 文件系统上构建。这在 Linux 上是微不足道的。您可能还希望在 RAM 文件系统上保留一份通用头文件(预编译的或实际的 .h 文件)的副本。
  • 预编译头文件。我每个(主要)库(例如 Boost、Qt、stdlib)都有一个。
  • 在可能的情况下声明而不是包含类。这减少了依赖性,从而减少了更改头文件时需要重新编译的文件数量。
  • 并行化 make。这通常会根据具体情况有所帮助,但我在-j3全球范围内都有make。但是,请确保您的 Makefile 中的依赖关系图是正确的,否则您可能会遇到问题。
  • 使用-O0,如果你不测试执行速度或代码大小(和你的电脑速度不够快,你没有太多在意(可能小)的性能损失)。
  • 每次保存都要编译​​。有些人不喜欢这样,但它可以让您及早发现错误并且可以在后台完成,从而减少您在完成编写和准备测试时必须等待的时间。

回答by Nordic Mainframe

I assume that we are talking about minutesto compile a file, i.e. precompiled headers or local disk issues aren't the problem.

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

Long compilation times with deep template code (boost etc.) is often rooted in the unfriendly asymptotic behavior of gcc when it comes to template instantiation, in particular when variadic templates are emulated with template default arguments.

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

Here's a document which names reduced compilation time as a motivation for variadic templates:

这是一个文档,其中将缩短编译时间称为可变参数模板的动机:

cpptruths had an article about how gcc-4.5 is much better in this behalf and how it does brilliantly with its variadic templates:

cpptruths 有一篇关于 gcc-4.5 如何在这方面更好的文章以及它如何出色地使用其可变参数模板:

IIRC then BOOST has a way to limit the generation of template default parameters for the pseudo-variadics, I think 'g++ -DBOOST_MPL_LIMIT_LIST_SIZE=10' should work (the default is 20)

IIRC 然后 BOOST 有一种方法来限制伪可变参数的模板默认参数的生成,我认为 'g++ -DBOOST_MPL_LIMIT_LIST_SIZE=10' 应该可以工作(默认为 20)

UPDATE:There is also a nice thread with general techniques to speed up compiling here on SO which might be useful:

更新:还有一个很好的线程,其中包含通用技术可以在 SO 上加速编译,这可能很有用:

UPDATE:This one is about the performance issues when compiling templates, the accepted answer recommends gcc-4.5 too, also clang is mentioned as a positive example:

更新:这是关于编译模板时的性能问题,接受的答案也推荐 gcc-4.5,还提到了 clang 作为一个正面例子:

回答by Sam Miller

Here's what I've done to speed up builds under a very similar scenario that you describe (boost, templates, gcc)

这是我在您描述的非常相似的场景下为加速构建所做的工作(提升、模板、gcc)

  • build on local disk instead of a network file system like NFS
  • upgrade to a newer version of gcc
  • investigate distcc
  • faster build systems, especially more RAM
  • 在本地磁盘上构建,而不是像 NFS 这样的网络文件系统
  • 升级到更新版本的 gcc
  • 调查的distcc
  • 更快的构建系统,尤其是更多的 RAM

回答by viraptor

If you're doing a lot of recompilation, ccachemight help. It doesn't actually speed up the compilation, but it will give you a cached result if you happen to do a useless recompilation for some reason. It might give an impression of tackling the wrong problem, but sometimes the rebuilding rules are so complicated that you actually do end up with the same compilation step during a new build.

如果您要进行大量重新编译,ccache可能会有所帮助。它实际上并没有加快编译速度,但是如果您由于某种原因碰巧进行了无用的重新编译,它会给您一个缓存的结果。它可能给人一种解决错误问题的印象,但有时重建规则非常复杂,以至于您实际上在新构建期间最终会执行相同的编译步骤。

Additional idea: if your code compiles with clang, use it instead. It's usually faster than gcc.

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

回答by utnapistim

On top of what everybody else added and what you're already doing (parallelized build, compiler options, etc), consider hiding templates in implementation classes, accessed through interfaces. That means that instead of having a class like:

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

// ClsWithNoTemplates.h file, included everywhere

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

public:
    void FunctionUsingYourMember();
};

you should have:

你应该有:

// 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
};

This changes your OOP design a bit, but it's for the good: including the definition of 'ClsWithNoTemplates' is now fastand you only (pre)compile the definition of 'ClsWithNoTemplates' once.

这会稍微改变您的 OOP 设计,但这是有益的:现在包含 'ClsWithNoTemplates' 的定义很快,您只需(预)编译一次 'ClsWithNoTemplates' 的定义。

Aditionally, if you change the implementation code, any code that included ClsWithNoTemplates.h will probably not need to be redefined.

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

This change should dramatically increase your partial compilation time, and it will also help in the case where your ClsWithNoTemplates is a public interface exported from a library file: since the file is not changed when you only change the implementation, your dependent client code doesn't need to be recompiled at all.

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

回答by gtrak

Try the PIMPL technique, this question: What techniques can be used to speed up C++ compilation times?

试试 PIMPL 技术,这个问题:什么技术可以用来加速 C++ 编译时间?

It'll prevent the compiler from following the chain of header files and implementations every time you need to do something.

每次您需要做某事时,它都会阻止编译器跟踪头文件和实现链。

回答by Zitrax

If there are a lot of files you can speed up compilation a lot by just having one .cpp file that #includes all the other .cpp files. This of course requires you to be more careful with macros and such that you already have defined per file as they will now be visible to other cpp files.

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

If there are many files this can reduce compile time a lot.

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

回答by Puppy

Instantiate less templates and inline functions. Precompile as much as you can and just link it rather than compiling everything from scratch. Make sure you're using the latest version of GCC.

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

However, it's a simple fact that C++ is an incredibly complex language and compiling it takes quite some time.

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

回答by Chris Tonkinson

This paperdescribes a method for compiling template code much like "traditional" non-template object files. Saves compile & link time, with only one line of code overhead per template instantiation.

本文描述了一种编译模板代码的方法,类似于“传统的”非模板目标文件。节省编译和链接时间,每个模板实例化只有一行代码开销。

回答by John R. Strohm

Usually, the most expensive parts of compilation are (a) reading the source files (ALLof them) and (b) loading the compiler into memory for each source file.

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

If you have 52 source (.cc) files, each of which #includes 47 #include (.h) files, you are going to load the compiler 52 times, and you are going to plow through 2496 files. Depending on the density of comments in the files, you may be spending a fair chunk of time eating useless characters. (In one organization I have seen, header files varied between 66% and 90% comments, with only 10%-33% of the file being "meaningful". The single best thing that could be done to enhance readability of those files was strip out every last comment, leaving only code.)

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

Take a long look at how your program is physically organized. See whether you can combine source files, and simplify your hierarchy of #include files.

仔细看看你的程序是如何组织起来的。查看是否可以合并源文件,并简化#include 文件的层次结构。

Decades ago, companies like IBM understood this, and would write their compilers so that the compiler could be handed a list of files to compile, not just one file, and the compiler would only be loaded once.

几十年前,像 IBM 这样的公司就明白这一点,并且会编写他们的编译器,以便编译器可以收到要编译的文件列表,而不仅仅是一个文件,并且编译器只会被加载一次。