可以使用哪些技术来加快 C++ 编译时间?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/373142/
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-27 15:01:38  来源:igfitidea点击:

What techniques can be used to speed up C++ compilation times?

c++

提问by Scott Langham

What techniques can be used to speed up C++ compilation times?

可以使用哪些技术来加快 C++ 编译时间?

This question came up in some comments to Stack Overflow question C++ programming style, and I'm interested to hear what ideas there are.

这个问题出现在 Stack Overflow 问题C++ 编程风格的一些评论中,我很想知道有什么想法。

I've seen a related question, Why does C++ compilation take so long?, but that doesn't provide many solutions.

我看过一个相关的问题,为什么 C++ 编译需要这么长时间?,但这并没有提供很多解决方案。

回答by Eclipse

Language techniques

语言技巧

Pimpl Idiom

Pimpl 成语

Take a look at the Pimpl idiomhere, and here, also known as an opaque pointeror handle classes. Not only does it speed up compilation, it also increases exception safety when combined with a non-throwing swapfunction. The Pimpl idiom lets you reduce the dependencies between headers and reduces the amount of recompilation that needs to be done.

看看这里Pimpl 惯用语这里,也称为不透明指针或句柄类。它不仅可以加快编译速度,还可以在与非抛出交换函数结合使用时提高异常安全性。Pimpl idiom 可让您减少头文件之间的依赖关系并减少需要完成的重新编译的数量。

Forward Declarations

前向声明

Wherever possible, use forward declarations. If the compiler only needs to know that SomeIdentifieris a struct or a pointer or whatever, don't include the entire definition, forcing the compiler to do more work than it needs to. This can have a cascading effect, making this way slower than they need to be.

在可能的情况下,使用前向声明。如果编译器只需要知道它SomeIdentifier是一个结构体或指针或其他什么,不要包含整个定义,迫使编译器做比它需要的更多的工作。这可能会产生级联效应,使这种方式比他们需要的要慢。

The I/Ostreams are particularly known for slowing down builds. If you need them in a header file, try #including <iosfwd>instead of <iostream>and #include the <iostream>header in the implementation file only. The <iosfwd>header holds forward declarations only. Unfortunately the other standard headers don't have a respective declarations header.

I / O流,特别是著名的减缓构建。如果您需要在头文件中使用它们,请尝试 #include<iosfwd>而不是仅在实现文件中使用<iostream>#include<iostream>头文件。该<iosfwd>头只拥有前置声明。不幸的是,其他标准标头没有相应的声明标头。

Prefer pass-by-reference to pass-by-value in function signatures. This will eliminate the need to #include the respective type definitions in the header file and you will only need to forward-declare the type. Of course, prefer const references to non-const references to avoid obscure bugs, but this is an issue for another question.

在函数签名中更喜欢按引用传递而不是按值传递。这将消除在头文件中 #include 相应类型定义的需要,您只需要向前声明类型。当然,更喜欢 const 引用而不是非常量引用以避免晦涩的错误,但这是另一个问题的问题。

Guard Conditions

保护条件

Use guard conditions to keep header files from being included more than once in a single translation unit.

使用保护条件来防止头文件在单个翻译单元中被多次包含。

#pragma once
#ifndef filename_h
#define filename_h

// Header declarations / definitions

#endif

By using both the pragma and the ifndef, you get the portability of the plain macro solution, as well as the compilation speed optimization that some compilers can do in the presence of the pragma oncedirective.

通过同时使用 pragma 和 ifndef,您可以获得普通宏解决方案的可移植性,以及某些编译器可以在存在pragma once指令的情况下进行的编译速度优化。

Reduce interdependency

减少相互依赖

The more modular and less interdependent your code design is in general, the less often you will have to recompile everything. You can also end up reducing the amount of work the compiler has to do on any individual block at the same time, by virtue of the fact that it has less to keep track of.

通常,您的代码设计越模块化且相互依赖越少,您需要重新编译所有内容的频率就越低。您还可以最终减少编译器同时在任何单个块上执行的工作量,因为它需要跟踪的内容更少。

Compiler options

编译器选项

Precompiled Headers

预编译头

These are used to compile a common section of included headers once for many translation units. The compiler compiles it once, and saves its internal state. That state can then be loaded quickly to get a head start in compiling another file with that same set of headers.

这些用于为许多翻译单元编译一次包含标题的公共部分。编译器编译一次,并保存其内部状态。然后可以快速加载该状态,以便在编译具有相同标头集的另一个文件方面领先一步。

Be careful that you only include rarely changed stuff in the precompiled headers, or you could end up doing full rebuilds more often than necessary. This is a good place for STLheaders and other library include files.

请注意,您只在预编译头文件中包含很少更改的内容,否则您最终可能会比必要更频繁地进行完全重建。这是存放STL头文件和其他库包含文件的好地方。

ccacheis another utility that takes advantage of caching techniques to speed things up.

ccache是另一个利用缓存技术来加快速度的实用程序。

Use Parallelism

使用并行性

Many compilers / IDEs support using multiple cores/CPUs to do compilation simultaneously. In GNU Make(usually used with GCC), use the -j [N]option. In Visual Studio, there's an option under preferences to allow it to build multiple projects in parallel. You can also use the /MPoptionfor file-level paralellism, instead of just project-level paralellism.

许多编译器/IDE 支持使用多个内核/CPU 同时进行编译。在GNU Make(通常与 GCC 一起使用)中,使用该-j [N]选项。在 Visual Studio 中,首选项下有一个选项,允许它并行构建多个项目。您还可以使用文件级并行/MP选项,而不仅仅是项目级并行。

Other parallel utilities:

其他并行实用程序:

Use a Lower Optimization Level

使用较低的优化级别

The more the compiler tries to optimize, the harder it has to work.

编译器尝试优化的次数越多,它就越难工作。

Shared Libraries

共享库

Moving your less frequently modified code into libraries can reduce compile time. By using shared libraries (.soor .dll), you can reduce linking time as well.

将不经常修改的代码移到库中可以减少编译时间。通过使用共享库(.so.dll),您还可以减少链接时间。

Get a Faster Computer

获得更快的计算机

More RAM, faster hard drives (including SSDs), and more CPUs/cores will all make a difference in compilation speed.

更多的 RAM、更快的硬盘驱动器(包括 SSD)和更多的 CPU/内核都会对编译速度产生影响。

回答by Mani Zandifar

I work on the STAPL project which is a heavily-templated C++ library. Once in a while, we have to revisit all the techniques to reduce compilation time. In here, I have summarized the techniques we use. Some of these techniques are already listed above:

我在 STAPL 项目上工作,它是一个大量模板化的 C++ 库。偶尔,我们必须重新审视所有技术以减少编译时间。在这里,我总结了我们使用的技术。其中一些技术已在上面列出:

Finding the most time-consuming sections

查找最耗时的部分

Although there is no proven correlation between the symbol lengths and compilation time, we have observed that smaller average symbol sizes can improve compilation time on all compilers. So your first goals it to find the largest symbols in your code.

尽管符号长度和编译时间之间没有经过证实的相关性,但我们观察到较小的平均符号大小可以改善所有编译器的编译时间。所以你的第一个目标是在你的代码中找到最大的符号。

Method 1 - Sort symbols based on size

方法 1 - 根据大小对符号进行排序

You can use the nmcommand to list the symbols based on their sizes:

您可以使用该nm命令根据符号的大小列出符号:

nm --print-size --size-sort --radix=d YOUR_BINARY

In this command the --radix=dlets you see the sizes in decimal numbers (default is hex). Now by looking at the largest symbol, identify if you can break the corresponding class and try to redesign it by factoring the non-templated parts in a base class, or by splitting the class into multiple classes.

在此命令中--radix=d,您可以查看十进制数的大小(默认为十六进制)。现在通过查看最大的符号,确定是否可以打破相应的类,并尝试通过分解基类中的非模板部分或将类拆分为多个类来重新设计它。

Method 2 - Sort symbols based on length

方法 2 - 根据长度对符号进行排序

You can run the regular nmcommand and pipe it to your favorite script (AWK, Python, etc.) to sort the symbols based on their length. Based on our experience, this method identifies the largest trouble making candidates better than method 1.

您可以运行常规nm命令并将其通过管道传输到您喜欢的脚本(AWKPython等)以根据它们的长度对符号进行排序。根据我们的经验,此方法确定了比方法 1 更好的候选人的最大麻烦。

Method 3 - Use Templight

方法 3 - 使用 Templight

"Templightis a Clang-based tool to profile the time and memory consumption of template instantiations and to perform interactive debugging sessions to gain introspection into the template instantiation process".

Templight是一个基于Clang的工具,用于分析模板实例化的时间和内存消耗,并执行交互式调试会话以深入了解模板实例化过程”。

You can install Templight by checking out LLVMand Clang (instructions) and applying the Templight patch on it. The default setting for LLVM and Clang is on debug and assertions, and these can impact your compilation time significantly. It does seem like Templight needs both, so you have to use the default settings. The process of installing LLVM and Clang should take about an hour or so.

您可以通过查看LLVM和 Clang(说明)并在其上应用 Templight 补丁来安装 Templight 。LLVM 和 Clang 的默认设置是调试和断言,这些会显着影响您的编译时间。看起来 Templight 需要两者,所以你必须使用默认设置。安装 LLVM 和 Clang 的过程大约需要一个小时左右。

After applying the patch you can use templight++located in the build folder you specified upon installation to compile your code.

应用补丁后,您可以使用templight++位于安装时指定的构建文件夹中来编译代码。

Make sure that templight++is in your PATH. Now to compile add the following switches to your CXXFLAGSin your Makefile or to your command line options:

确保它templight++在您的 PATH 中。现在要编译,请将以下开关添加到您CXXFLAGS的 Makefile 或命令行选项中:

CXXFLAGS+=-Xtemplight -profiler -Xtemplight -memory -Xtemplight -ignore-system

Or

或者

templight++ -Xtemplight -profiler -Xtemplight -memory -Xtemplight -ignore-system

After compilation is done, you will have a .trace.memory.pbf and .trace.pbf generated in the same folder. To visualize these traces, you can use the Templight Toolsthat can convert these to other formats. Follow these instructionsto install templight-convert. We usually use the callgrind output. You can also use the GraphViz output if your project is small:

编译完成后,您将在同一文件夹中生成 .trace.memory.pbf 和 .trace.pbf 。要可视化这些轨迹,您可以使用可以将这些轨迹转换为其他格式的Templight 工具。按照这些说明安装 templight-convert。我们通常使用 callgrind 输出。如果您的项目很小,您还可以使用 GraphViz 输出:

$ templight-convert --format callgrind YOUR_BINARY --output YOUR_BINARY.trace

$ templight-convert --format graphviz YOUR_BINARY --output YOUR_BINARY.dot

The callgrind file generated can be opened using kcachegrindin which you can trace the most time/memory consuming instantiation.

生成的 callgrind 文件可以使用kcachegrind打开,您可以在其中跟踪最耗时/内存消耗的实例化。

Reducing the number of template instantiations

减少模板实例化的数量

Although there are no exact solution for reducing the number of template instantiations, there are a few guidelines that can help:

虽然没有减少模板实例化数量的确切解决方案,但有一些指南可以提供帮助:

Refactor classes with more than one template arguments

使用多个模板参数重构类

For example, if you have a class,

例如,如果你有一个班级,

template <typename T, typename U>
struct foo { };

and both of Tand Ucan have 10 different options, you have increased the possible template instantiations of this class to 100. One way to resolve this is to abstract the common part of the code to a different class. The other method is to use inheritance inversion (reversing the class hierarchy), but make sure that your design goals are not compromised before using this technique.

两者的TU可以有10个不同的选项,你已经增加了这类可能的模板实例,以100的一种方式来解决,这是抽象的代码的公共部分,以不同的类。另一种方法是使用继承倒置(反转类层次结构),但在使用此技术之前请确保您的设计目标不会受到影响。

Refactor non-templated code to individual translation units

将非模板化代码重构为单个翻译单元

Using this technique, you can compile the common section once and link it with your other TUs (translation units) later on.

使用此技术,您可以一次编译公共部分,稍后将其与其他 TU(翻译单元)链接。

Use extern template instantiations (since C++11)

使用 extern 模板实例化 (C++11 起)

If you know all the possible instantiations of a class you can use this technique to compile all cases in a different translation unit.

如果您知道一个类的所有可能实例化,您可以使用此技术在不同的翻译单元中编译所有案例。

For example, in:

例如,在:

enum class PossibleChoices = {Option1, Option2, Option3}

template <PossibleChoices pc>
struct foo { };

We know that this class can have three possible instantiations:

我们知道这个类可以有三种可能的实例化:

template class foo<PossibleChoices::Option1>;
template class foo<PossibleChoices::Option2>;
template class foo<PossibleChoices::Option3>;

Put the above in a translation unit and use the extern keyword in your header file, below the class definition:

将上述内容放在翻译单元中,并在头文件中的类定义下方使用 extern 关键字:

extern template class foo<PossibleChoices::Option1>;
extern template class foo<PossibleChoices::Option2>;
extern template class foo<PossibleChoices::Option3>;

This technique can save you time if you are compiling different tests with a common set of instantiations.

如果您使用一组通用实例编译不同的测试,则此技术可以节省您的时间。

NOTE : MPICH2 ignores the explicit instantiation at this point and always compiles the instantiated classes in all compilation units.

注意:此时 MPICH2 会忽略显式实例化,并始终编译所有编译单元中的实例化类。

Use unity builds

使用统一构建

The whole idea behind unity builds is to include all the .cc files that you use in one file and compile that file only once. Using this method, you can avoid reinstantiating common sections of different files and if your project includes a lot of common files, you probably would save on disk accesses as well.

统一构建背后的整个想法是将您使用的所有 .cc 文件包含在一个文件中,并且只编译该文件一次。使用这种方法,您可以避免重新实例化不同文件的公共部分,如果您的项目包含大量公共文件,您可能还会节省磁盘访问。

As an example, let's assume you have three files foo1.cc, foo2.cc, foo3.ccand they all include tuplefrom STL. You can create a foo-all.ccthat looks like:

例如,假设您有三个文件foo1.cc, foo2.ccfoo3.cc并且它们都tuple来自STL。您可以创建一个foo-all.cc看起来像:

#include "foo1.cc"
#include "foo2.cc"
#include "foo3.cc"

You compile this file only once and potentially reduce the common instantiations among the three files. It is hard to generally predict if the improvement can be significant or not. But one evident fact is that you would loseparallelism in your builds (you can no longer compile the three files at the same time).

您只需编译此文件一次,就可能会减少三个文件之间的常见实例化。通常很难预测改进是否显着。但一个明显的事实是,您将在构建中失去并行性(您不能再同时编译这三个文件)。

Further, if any of these files happen to take a lot of memory, you might actually run out of memory before the compilation is over. On some compilers, such as GCC, this might ICE (Internal Compiler Error) your compiler for lack of memory. So don't use this technique unless you know all the pros and cons.

此外,如果这些文件中的任何一个碰巧占用了大量内存,您实际上可能会在编译结束之前耗尽内存。在某些编译器上,例如GCC,这可能会因内存不足而导致编译器 ICE(内部编译器错误)。所以除非你知道所有的利弊,否则不要使用这种技术。

Precompiled headers

预编译头

Precompiled headers (PCHs) can save you a lot of time in compilation by compiling your header files to an intermediate representation recognizable by a compiler. To generate precompiled header files, you only need to compile your header file with your regular compilation command. For example, on GCC:

通过将头文件编译为编译器可识别的中间表示,预编译头文件 (PCH) 可以为您节省大量编译时间。要生成预编译头文件,您只需要使用常规编译命令编译头文件。例如,在 GCC 上:

$ g++ YOUR_HEADER.hpp

This will generate a YOUR_HEADER.hpp.gch file(.gchis the extension for PCH files in GCC) in the same folder. This means that if you include YOUR_HEADER.hppin some other file, the compiler will use your YOUR_HEADER.hpp.gchinstead of YOUR_HEADER.hppin the same folder before.

这将在同一文件夹中生成一个YOUR_HEADER.hpp.gch file.gch是 GCC 中 PCH 文件的扩展名)。这意味着如果你包含YOUR_HEADER.hpp在其他文件中,编译器将使用你的YOUR_HEADER.hpp.gch而不是YOUR_HEADER.hpp之前在同一个文件夹中。

There are two issues with this technique:

这种技术有两个问题:

  1. You have to make sure that the header files being precompiled is stable and is not going to change (you can always change your makefile)
  2. You can only include one PCH per compilation unit (on most of compilers). This means that if you have more than one header file to be precompiled, you have to include them in one file (e.g., all-my-headers.hpp). But that means that you have to include the new file in all places. Fortunately, GCC has a solution for this problem. Use -includeand give it the new header file. You can comma separate different files using this technique.
  1. 你必须确保被预编译的头文件是稳定的并且不会改变(你总是可以改变你的 makefile
  2. 每个编译单元只能包含一个 PCH(在大多数编译器上)。这意味着如果您有多个头文件要预编译,您必须将它们包含在一个文件中(例如,all-my-headers.hpp)。但这意味着您必须在所有位置都包含新文件。幸运的是,GCC 有针对这个问题的解决方案。使用-include并给它新的头文件。您可以使用此技术用逗号分隔不同的文件。

For example:

例如:

g++ foo.cc -include all-my-headers.hpp

Use unnamed or anonymous namespaces

使用未命名或匿名命名空间

Unnamed namespaces(a.k.a. anonymous namespaces) can reduce the generated binary sizes significantly. Unnamed namespaces use internal linkage, meaning that the symbols generated in those namespaces will not be visible to other TU (translation or compilation units). Compilers usually generate unique names for unnamed namespaces. This means that if you have a file foo.hpp:

未命名的命名空间(又名匿名命名空间)可以显着减少生成的二进制大小。未命名命名空间使用内部链接,这意味着在这些命名空间中生成的符号对其他 TU(翻译或编译单元)不可见。编译器通常为未命名的命名空间生成唯一的名称。这意味着如果你有一个文件 foo.hpp:

namespace {

template <typename T>
struct foo { };
} // Anonymous namespace
using A = foo<int>;

And you happen to include this file in two TUs (two .cc files and compile them separately). The two foo template instances will not be the same. This violates the One Definition Rule(ODR). For the same reason, using unnamed namespaces is discouraged in the header files. Feel free to use them in your .ccfiles to avoid symbols showing up in your binary files. In some cases, changing all the internal details for a .ccfile showed a 10% reduction in the generated binary sizes.

并且您碰巧将此文件包含在两个 TU 中(两个 .cc 文件并分别编译它们)。两个 foo 模板实例不会相同。这违反了单一定义规则(ODR)。出于同样的原因,不鼓励在头文件中使用未命名的命名空间。随意在您的.cc文件中使用它们以避免符号出现在您的二进制文件中。在某些情况下,更改文件的所有内部细节会.cc显示生成的二进制大小减少 10%。

Changing visibility options

更改可见性选项

In newer compilers you can select your symbols to be either visible or invisible in the Dynamic Shared Objects (DSOs). Ideally, changing the visibility can improve compiler performance, link time optimizations (LTOs), and generated binary sizes. If you look at the STL header files in GCC you can see that it is widely used. To enable visibility choices, you need to change your code per function, per class, per variable and more importantly per compiler.

在较新的编译器中,您可以选择在动态共享对象 (DSO) 中可见或不可见的符号。理想情况下,更改可见性可以提高编译器性能、链接时间优化 (LTO) 和生成的二进制大小。如果您查看 GCC 中的 STL 头文件,您会发现它被广泛使用。要启用可见性选择,您需要更改每个函数、每个类、每个变量的代码,更重要的是每个编译器。

With the help of visibility you can hide the symbols that you consider them private from the generated shared objects. On GCC you can control the visibility of symbols by passing default or hidden to the -visibilityoption of your compiler. This is in some sense similar to the unnamed namespace but in a more elaborate and intrusive way.

借助可见性,您可以从生成的共享对象中隐藏您认为私有的符号。在 GCC 上,您可以通过将 default 或 hidden 传递给-visibility编译器的选项来控制符号的可见性。这在某种意义上类似于未命名的命名空间,但以更复杂和侵入性的方式。

If you would like to specify the visibilities per case, you have to add the following attributes to your functions, variables, and classes:

如果您想指定每个案例的可见性,您必须将以下属性添加到您的函数、变量和类中:

__attribute__((visibility("default"))) void  foo1() { }
__attribute__((visibility("hidden")))  void  foo2() { }
__attribute__((visibility("hidden")))  class foo3   { };
void foo4() { }

The default visibility in GCC is default (public), meaning that if you compile the above as a shared library (-shared) method, foo2and class foo3will not be visible in other TUs (foo1and foo4will be visible). If you compile with -visibility=hiddenthen only foo1will be visible. Even foo4would be hidden.

GCC 中的默认可见性是 default (public),这意味着如果您将上述编译为共享库 ( -shared) 方法,则foo2foo3在其他 TU 中将不可见(foo1并且foo4将可见)。如果你编译,-visibility=hidden那么只有foo1可见。甚至foo4会被隐藏。

You can read more about visibility on GCC wiki.

您可以在GCC wiki上阅读有关可见性的更多信息。

回答by Paulius

I'd recommend these articles from "Games from Within, Indie Game Design And Programming":

我推荐这些来自“内部游戏,独立游戏设计和编程”的文章:

Granted, they are pretty old - you'll have to re-test everything with the latest versions (or versions available to you), to get realistic results. Either way, it is a good source for ideas.

诚然,它们已经很旧了 - 您必须使用最新版本(或您可用的版本)重新测试所有内容,以获得真实的结果。无论哪种方式,它都是一个很好的想法来源。

回答by Frerich Raabe

One technique which worked quite well for me in the past: don't compile multiple C++ source files independently, but rather generate one C++ file which includes all the other files, like this:

过去对我来说效果很好的一种技术:不要独立编译多个 C++ 源文件,而是生成一个包含所有其他文件的 C++ 文件,如下所示:

// myproject_all.cpp
// Automatically generated file - don't edit this by hand!
#include "main.cpp"
#include "mainwindow.cpp"
#include "filterdialog.cpp"
#include "database.cpp"

Of course this means you have to recompile all of the included source code in case any of the sources changes, so the dependency tree gets worse. However, compiling multiple source files as one translation unit is faster (at least in my experiments with MSVCand GCC) and generates smaller binaries. I also suspect that the compiler is given more potential for optimizations (since it can see more code at once).

当然,这意味着您必须重新编译所有包含的源代码,以防任何源代码发生更改,因此依赖关系树会变得更糟。但是,将多个源文件编译为一个翻译单元会更快(至少在我使用MSVC和 GCC 的实验中)并且生成更小的二进制文件。我还怀疑编译器被赋予了更多的优化潜力(因为它可以一次看到更多的代码)。

This technique breaks in various cases; for instance, the compiler will bail out in case two or more source files declare a global function with the same name. I couldn't find this technique described in any of the other answers though, that's why I'm mentioning it here.

这种技术在各种情况下都会失效;例如,如果两个或多个源文件声明了一个同名的全局函数,编译器就会退出。不过,我在其他任何答案中都找不到这种技术,这就是我在这里提到它的原因。

For what it's worth, the KDE Projectused this exact same technique since 1999 to build optimized binaries (possibly for a release). The switch to the build configure script was called --enable-final. Out of archaeological interest I dug up the posting which announced this feature: http://lists.kde.org/?l=kde-devel&m=92722836009368&w=2

值得一提的是,KDE 项目自 1999 年以来就使用完全相同的技术来构建优化的二进制文件(可能用于发布)。调用构建配置脚本的切换--enable-final。出于考古兴趣,我挖出了宣布此功能的帖子:http: //lists.kde.org/?l= kde-devel&m=92722836009368&w=2

回答by ChrisW

There's an entire book on this topic, which is titled Large-Scale C++ Software Design(written by John Lakos).

关于这个主题有一整本书,标题为“大规模 C++ 软件设计”(由 John Lakos 编写)。

The book pre-dates templates, so to the contents of that book add "using templates, too, can make the compiler slower".

这本书早于模板,因此在那本书的内容中添加“使用模板也会使编译器变慢”。

回答by Johannes Schaub - litb

I will just link to my other answer: How do YOU reduce compile time, and linking time for Visual C++ projects (native C++)?. Another point I want to add, but which causes often problems is to use precompiled headers. But please, only use them for parts which hardly ever change (like GUI toolkit headers). Otherwise, they will cost you more time than they save you in the end.

我将链接到我的另一个答案:您如何减少 Visual C++ 项目(本机 C++)的编译时间和链接时间?. 我想补充的另一点是使用预编译的头文件,但通常会导致问题。但是,请仅将它们用于几乎不会改变的部分(例如 GUI 工具包标题)。否则,它们会花费您更多的时间,而不是最终为您节省的时间。

Another option is, when you work with GNU make, to turn on -j<N>option:

另一种选择是,当您使用 GNU make 时,打开-j<N>选项:

  -j [N], --jobs[=N]          Allow N jobs at once; infinite jobs with no arg.

I usually have it at 3since I've got a dual core here. It will then run compilers in parallel for different translation units, provided there are no dependencies between them. Linking cannot be done in parallel, since there is only one linker process linking together all object files.

我通常使用它,3因为我这里有双核。然后它将为不同的翻译单元并行运行编译器,前提是它们之间没有依赖关系。链接不能并行完成,因为只有一个链接器进程将所有目标文件链接在一起。

But the linker itself can be threaded, and this is what the GNU goldELFlinker does. It's optimized threaded C++ code which is said to link ELF object files a magnitude faster than the old ld(and was actually included into binutils).

但是链接器本身可以被线程化,这就是ELF链接器所做的。它是优化的线程 C++ 代码,据说它链接 ELF 对象文件的速度比旧的快得多(实际上已包含在binutils 中)。GNU goldld

回答by Milan Babu?kov

Here are some:

这里有一些:

  • Use all processor cores by starting a multiple-compile job (make -j2is a good example).
  • Turn off or lower optimizations (for example, GCC is much faster with -O1than -O2or -O3).
  • Use precompiled headers.
  • 通过启动多重编译作业来使用所有处理器内核(make -j2是一个很好的例子)。
  • 关闭或降低优化(例如,GCC-O1-O2或快得多-O3)。
  • 使用预编译的头文件

回答by David Rodríguez - dribeas

Once you have applied all the code tricks above (forward declarations, reducing header inclusion to the minimum in public headers, pushing most details inside the implementation file with Pimpl...) and nothing else can be gained language-wise, consider your build system. If you use Linux, consider using distcc(distributed compiler) and ccache(cache compiler).

一旦您应用了上述所有代码技巧(前向声明,将公共头文件中的头文件包含减少到最少,使用Pimpl将大多数细节推送到实现文件中......)并且在语言方面无法获得其他任何东西,请考虑您的构建系统. 如果您使用 Linux,请考虑使用distcc(分布式编译器)和ccache(缓存编译器)。

The first one, distcc, executes the preprocessor step locally and then sends the output to the first available compiler in the network. It requires the same compiler and library versions in all the configured nodes in the network.

第一个,distcc,在本地执行预处理器步骤,然后将输出发送到网络中第一个可用的编译器。它要求网络中所有配置的节点具有相同的编译器和库版本。

The latter, ccache, is a compiler cache. It again executes the preprocessor and then check with an internal database (held in a local directory) if that preprocessor file has already been compiled with the same compiler parameters. If it does, it just pops up the binary and output from the first run of the compiler.

后者,ccache,是一个编译器缓存。它再次执行预处理器,然后检查内部数据库(保存在本地目录中)是否已经使用相同的编译器参数编译了该预处理器文件。如果是,它只是弹出二进制文件和编译器第一次运行的输出。

Both can be used at the same time, so that if ccache does not have a local copy it can send it trough the net to another node with distcc, or else it can just inject the solution without further processing.

两者可以同时使用,因此如果 ccache 没有本地副本,它可以通过网络将它发送到带有 distcc 的另一个节点,否则它可以直接注入解决方案而无需进一步处理。

回答by questzen

When I came out of college, the first real production-worthy C++ code I saw had these arcane #ifndef ... #endif directives in between them where the headers were defined. I asked the guy who was writing the code about these overarching things in a very naive fashion and was introduced to world of large-scale programming.

当我大学毕业时,我看到的第一个真正值得生产的 C++ 代码有这些神秘的 #ifndef ... #endif 指令,它们之间定义了标头。我以一种非常幼稚的方式向正在编写有关这些总体事物的代码并被介绍到大规模编程世界的人进行了询问。

Coming back to the point, using directives to prevent duplicate header definitions was the first thing I learned when it came to reducing compiling times.

回到正题,使用指令来防止重复的头定义是我在减少编译时间时学到的第一件事。

回答by mr calendar

More RAM.

更多内存。

Someone talked about RAM drives in another answer. I did this with a 80286and Turbo C++(shows age) and the results were phenomenal. As was the loss of data when the machine crashed.

有人在另一个答案中谈到了 RAM 驱动器。我用80286Turbo C++(显示年龄)做了这个,结果是惊人的。就像机器崩溃时数据丢失一样。