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


249

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

这个问题出现在Stack Overflow问题C ++编程风格的一些注释中,我很想听听那里有什么想法。

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


1
你能给我们些背景吗?还是您在寻找非常笼统的答案?
Pyrolistical

1
非常类似于这样的问题:stackoverflow.com/questions/364240/...
亚当罗森菲尔德

一般答案。我有很多人编写的非常庞大的代码库。关于如何进行攻击的想法将是不错的。而且,对于新编写的代码保持快速编译的建议也将很有趣。
Scott Langham

请注意,编译器通常不使用构建时间的相关部分,而是通过构建脚本使用这些时间
thi gg 2015年

1
我浏览了此页面,但没有提及任何度量。我编写了一个小shell脚本,在接收到的每一行输入中添加了时间戳,因此我可以通过管道传递“ make”调用。这使我仅通过比较时间戳即可了解哪些目标最昂贵,总编译或链接时间等。如果尝试这种方法,请记住,并行构建的时间戳是不准确的。
约翰·P

Answers:


257

语言技巧

皮普尔成语

此处此处(也称为不透明指针或句柄类)查看Pimpl习惯用法 。与非抛出交换结合使用时,它不仅可以加快编译速度,而且还可以提高异常安全性函数性。Pimpl习惯用法使您可以减少头文件之间的依赖性,并减少需要完成的重新编译量。

前瞻性声明

尽可能使用前向声明。如果编译器只需要知道它SomeIdentifier是结构,指针还是其他东西,就不要包含整个定义,从而迫使编译器执行比其所需更多的工作。这会产生级联效应,使这种方式的速度慢于所需的速度。

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

在函数签名中,首选传递引用比传递值。这样就无需在头文件中#include各自的类型定义,并且只需要向前声明该类型。当然,最好使用const引用而不是非const引用,以避免模糊的bug,但这是另一个问题。

警卫条件

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

#pragma once
#ifndef filename_h
#define filename_h

// Header declarations / definitions

#endif

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

减少相互依赖

通常,代码设计的模块化程度越高,相互依赖性越小,则重新编译所有内容的频率就越少。您还可以减少编译器必须同时跟踪任何单个块的工作量,从而减少了工作量。

编译器选项

预编译头

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

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

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

使用并行

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

其他并行实用程序:

使用较低的优化级别

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

共享库

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

获得更快的计算机

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


11
虽然预编译的头文件并不完美。使用它们的副作用是,包含的文件超出了必要的数量(因为每个编译单元都使用相同的预编译头),这可能会迫使完全重新编译的次数比必要的次数更多。只是要记住一点。
jalf

8
在现代编译器中,#ifndef与#pragma一次一样快(只要include保护位于文件的顶部)。所以这是在编译速度方面一度没有好处的#pragma
jalf

7
即使您只有VS 2005,而没有VS 2008,也可以在编译选项中添加/ MP开关,以在.cpp级别启用并行构建。
macbirdie

6
编写此答案时,SSD的价格过高,但如今,它们是编译C ++时的最佳选择。编译时,您访问许多小文件。这需要大量的IOPS,而SSD需要提供这些IOPS。
MSalters

14
在函数签名中,首选传递引用比传递值。这将消除在头文件中#include各自类型定义的需要。这是错误的,您不需要具有完整类型来声明按值传递的函数,只需要完整类型即可实现使用该功能。 ,但在大多数情况下(除非您仅转接呼叫),您仍然需要该定义。
大卫·罗德里格斯(DavidRodríguez)-dribeas,2011年

43

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

查找最耗时的部分

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

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

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

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

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

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

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

方法3-使用临时灯

Templight基于的工具,用于分析模板实例化的时间和内存消耗,并执行交互式调试会话以对模板实例化过程进行自省。”

您可以通过签出LLVM和Clang(说明)并在其上应用Templight补丁来安装Templight 。LLVM和Clang的默认设置是在调试和断言上,它们会极大地影响您的编译时间。看起来Templight两者都需要,因此您必须使用默认设置。安装LLVM和Clang的过程大约需要一个小时。

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

确保这templight++在您的PATH中。现在进行编译,将以下开关添加到CXXFLAGSMakefile中或命令行选项中:

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

要么

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

编译完成后,您将在同一文件夹中生成一个.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

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

减少模板实例化的数量

尽管没有减少模板实例化数量的确切解决方案,但是有一些准则可以帮助您:

使用多个模板参数重构类

例如,如果您有一堂课,

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

并且,TU都可以有10个不同的选项,您已经将该类的可能的模板实例化增加到100。解决此问题的一种方法是将代码的公共部分抽象到另一个类。另一种方法是使用继承反转(反转类层次结构),但是在使用此技术之前,请确保不损害您的设计目标。

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

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

使用外部模板实例化(自C ++ 11起)

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

例如,在:

enum class PossibleChoices = {Option1, Option2, Option3}

template <PossibleChoices pc>
struct foo { };

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

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

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

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

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

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

使用统一版本

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

举个例子,假设你有三个文件foo1.ccfoo2.ccfoo3.cc和它们都包括tupleSTL。您可以创建foo-all.cc如下所示的:

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

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

此外,如果这些文件中的任何一个恰好占用大量内存,则在编译结束之前您实际上可能会用完内存。在某些编译器(例如GCC)上,由于内存不足,这可能会导致ICE(内部编译器错误)编译器。因此,除非您了解所有优点和缺点,否则不要使用此技术。

预编译头

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

$ g++ YOUR_HEADER.hpp

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

此技术有两个问题:

  1. 您必须确保要预编译的头文件稳定且不会更改(您始终可以更改makefile
  2. 每个编译单元只能包含一个PCH(在大多数编译器上)。这意味着,如果要预编译多个头文件,则必须将它们包括在一个文件中(例如all-my-headers.hpp)。但这意味着您必须在所有位置包括新文件。幸运的是,GCC可以解决此问题。使用-include并给它新的头文件。您可以使用此技术逗号分隔不同的文件。

例如:

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

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

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

namespace {

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

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

更改可见性选项

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

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

如果要指定每种情况的可视性,则必须在函数,变量和类中添加以下属性:

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

GCC中的默认可见性为默认(公共),这意味着如果将上述内容编译为共享库(-shared)方法,则foo2该类foo3在其他TU中将不可见(foo1并且foo4将可见)。如果您使用进行编译,-visibility=hidden则只会foo1显示。甚至foo4会被隐藏。

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


33

我建议从“内部游戏,独立游戏设计和编程”中推荐以下文章:

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


17

过去对我来说效果很好的一种技术是:不要独立地编译多个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"

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

这种技术在各种情况下都会失效。例如,如果两个或多个源文件声明一个具有相同名称的全局函数,则编译器将进行纾困。我找不到其他答案中描述的这项技术,这就是为什么我在这里提到它。

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


2
我不确定这是否真的是同一回事,但是我猜想在VC ++(msdn.microsoft.com/en-us/library/0zza0de8%28VS.71%29.aspx)中打开“整个程序优化” 对运行时性能的影响与您建议的相同。但是,编译时绝对可以改善您的方法!
菲利普

1
@Frerich:您正在描述OJ的答案中提到的Unity构建。我也看到过它们称为批量构建和主构建。
idbrii 2012年

那么,UB与WPO / LTCG相比如何?
保罗

这可能仅对一次性编译有用,而对于在编辑,构建和测试之间循环的开发阶段则无效。在现代世界中,四核是常态,也许几年后,核数会大大增加。如果编译器和链接器无法利用多个线程,则文件列表可能会拆分为<core-count> + N并行编译的子列表,其中子列表N是一些合适的整数(取决于系统内存和机器的使用方式)。
FooF '16

15

关于此主题有一整本书,标题为《大规模C ++软件设计》(由John Lakos撰写)。

这本书比模板早,所以在那本书的内容上加上“也使用模板,会使编译器变慢”。


该书经常在此类主题中提及,但对我而言,它的信息很少。它基本上声明尽可能多地使用前向声明并解耦依赖关系。除了使用pimpl习惯用法还存在运行时缺陷之外,这还说明了一些显而易见的内容。
gast128

@ gast128我认为其要点是使用允许增量式重新编译的编码惯用语,即,因此,如果您在某处更改了一点源代码,则无需重新编译所有内容。
ChrisW

15

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

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

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

我通常在这里,3因为我在这里有一个双核。如果它们之间没有依赖性,它将为不同的翻译单元并行运行编译器。链接不能并行完成,因为只有一个链接程序进程将所有目标文件链接在一起。

但是链接器本身可以被线程化,这就是ELF链接器的作用。它是经过优化的线程化C ++代码,据说它链接ELF目标文件的速度比旧版本快(实际上已包含在binutils中)。GNU gold ld


好的。抱歉,我搜索时没有出现这个问题。
Scott Langham

您不必后悔。那是为Visual C ++。您的问题似乎针对任何编译器。这样就可以了:)
Johannes Schaub-litb

12

这里有一些:

  • 通过开始多重编译作业来使用所有处理器内核(这make -j2是一个很好的例子)。
  • 关闭或降低优化效果(例如,使用或-O1比GCC快得多)。-O2-O3
  • 使用预编译的头文件

12
仅供参考,我发现启动更多的进程通常比启动核心更快。例如,在四核系统上,我通常使用-j8,而不是-j4。这样做的原因是,当一个进程在I / O上被阻塞时,另一个进程可以进行编译。
Fooz先生08年

@MrFooz:我几年前通过在i7-2700k(4个内核,8个线程,我设置了一个常数倍增器)上编译Linux内核(来自RAM存储)来测试了这一点。我忘了确切的最好成绩,但-j12各地-j18均大大高于-j8,就像你的建议。我想知道在内存带宽成为限制因素之前可以拥有多少个内核...
Mark K Cowan 2015年

@MarkKCowan它取决于很多因素。不同的计算机具有截然不同的内存带宽。如今,使用高端处理器,需要多个内核来使内存总线饱和。此外,在I / O和CPU之间保持平衡。有些代码很容易编译,而另一些代码则可能很慢(例如,有很多模板)。我目前的经验法则是-j实际核心数的2倍。
福兹先生2015年

11

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

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

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

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


2
我认为distcc 在所有已配置的节点上都不需要相同的版本。distcc仅远程进行编译,而不进行链接。它还通过网络发送预处理的代码,因此远程系统上可用的标头无关紧要。
Frerich Raabe 2012年

9

当我大学毕业时,我看到的第一个真正有价值的C ++代码在它们之间的标头定义位置包含了这些奥秘的#ifndef ... #endif指令。我问那个以非常幼稚的方式编写代码的有关这些总体问题的家伙,并被介绍给大规模编程领域。

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


1
老但黄金。有时,显而易见的事情被遗忘了。
alcor 2014年

1
'包括卫兵'
gast128 '19

8

更多RAM。

有人在另一个答案中谈到了RAM驱动器。我使用80286Turbo C ++(显示年龄)进行了此操作,结果非常出色。机器崩溃时数据丢失也是如此。


在DOS中虽然没有多少内存
phuclv

6

尽可能使用前向声明。如果类声明仅使用对类型的指针或引用,则可以向前声明它,并将类型的标头包括在实现文件中。

例如:

// T.h
class Class2; // Forward declaration

class T {
public:
    void doSomething(Class2 &c2);
private:
    Class2 *m_Class2Ptr;
};

// T.cpp
#include "Class2.h"
void Class2::doSomething(Class2 &c2) {
    // Whatever you want here
}

如果包含足够多的内容,则意味着预处理器的工作量将大大减少。


这仅在几个翻译单元中包含相同的标头时才有意义吗?如果只有一个翻译单元(通常使用模板时就是这种情况),那么这似乎没有任何影响。
AlwaysLearning

1
如果只有一个翻译单元,为什么还要把它放在标题中呢?只将内容放在源文件中更有意义吗?标头的全部意思不是它可能包含在多个源文件中吗?
埃文·特兰


5

#pragma once

头文件的顶部,因此,如果它们在翻译单元中被多次包含,则头文件的文本将仅被包含并解析一次。


2
尽管广泛支持,但是#pragma曾经是非标准的。参见en.wikipedia.org/wiki/Pragma_once
ChrisInEdmonton,

7
而如今,定期收看守卫也有同样的效果。只要它们位于文件的顶部,编译器就完全能够将它们一次视为#pragma
jalf


4
  • 升级电脑

    1. 获取四核(或双四核系统)
    2. 获取大量的RAM。
    3. 使用RAM驱动器可以大大减少文件I / O延迟。(有些公司制造的IDE和SATA RAM驱动器的作用类似于硬盘驱动器)。
  • 然后,您还有所有其他典型建议

    1. 使用可用的预编译头。
    2. 减少项目各部分之间的耦合量。更改一个头文件通常不需要重新编译整个项目。

4

我有一个使用RAM驱动器的想法。事实证明,对于我的项目而言,毕竟没有什么太大的不同。但是那时它们还很小。试试吧!我想听听它有多大帮助。


嗯 为什么有人对此投下反对票?我明天去试试。
Scott Langham

1
我希望这次投票是因为它不会带来太大的改变。如果您有足够的未使用RAM,则OS仍将智能地将其用作磁盘缓存。
MSalters

1
@MSalters-“足够”多少钱?我知道这就是理论,但是由于某种原因,使用RAMdrive 确实可以带来很大的提升。走吧...
Vilx-

1
足以编译您的项目并仍然缓存输入文件和临时文件。显然,GB的一面将直接取决于您的项目大小。应该注意的是,在较旧的操作系统(尤其是WinXP)上,文件缓存非常懒惰,没有使用RAM。
MSalters 2010年

如果文件已经在ram中,而不是先进行一堆缓慢的IO,那么ram驱动器肯定会更快,然后它们在ram中了吗?(对于已更改的文件重复执行-将其写回到磁盘等)。
paulm

3

动态链接(.so)比静态链接(.a)快得多。特别是当您的网络驱动器速度较慢时。这是因为您在.a文件中拥有所有需要处理和写出的代码。另外,需要将更大的可执行文件写出到磁盘。


动态链接会阻止多种链接时间优化,因此在许多情况下输出可能会变慢
phuclv

3

与编译时间无关,而与构建时间有关:

  • 如果在构建文件时必须重建相同的文件,请使用ccache

  • ninja-build代替make。我目前正在用〜100个源文件编译一个项目,并且所有内容都由ccache缓存。需要5分钟,忍者不到1分钟。

您可以使用来从cmake生成忍者文件-GNinja


3

你在哪里消磨时间?您是否受CPU限制?内存受限?磁盘绑定?您可以使用更多核心吗?更多RAM?您需要RAID吗?您是否只是想提高当前系统的效率?

在gcc / g ++下,您看过ccache吗?如果您要做make clean; make很多事情,这可能会有所帮助。


2

更快的硬盘。

编译器将许多(可能是巨大的)文件写入磁盘。使用SSD代替典型的硬盘,编译时间要短得多。



2

网络共享将极大地减慢您的构建速度,因为查找延迟很高。对于Boost之类的东西,即使我们的网络共享速度非常快,它对我也产生了巨大的影响。当我从网络共享切换到本地SSD时,编译玩具Boost程序的时间从大约1分钟缩短到1秒。


2

如果您有多核处理器,则Visual Studio(2005和更高版本)以及GCC都支持多处理器编译。当然,如果您具有硬件,则可以启用该功能。


2
@Fellman,请参阅其他答案-使用-j#选项。
斯特拉格

1

尽管不是“技术”,但我无法弄清楚带有许多源文件的Win32项目如何比“ Hello World”空项目更快地编译。因此,我希望这对像我这样的人有所帮助。

在Visual Studio中,增加编译时间的一种方法是增量链接(/ INCREMENTAL)。它与链接时代码生成(/ LTCG)不兼容,因此请记住在发布版本时禁用增量链接。


1
禁用链接时代码生成不是一个好建议,因为它会禁用许多优化。您只需要/INCREMENTAL在调试模式下启用
phuclv


0

使用动态链接而不是静态链接可以使编译器感觉更快。

如果使用t Cmake,请激活该属性:

set(BUILD_SHARED_LIBS ON)

使用静态链接的Build Release可以获得更多优化。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.