如何在Visual C++项目(本地C++)中缩短编译时间和链接时间?

42

您如何缩短VC++项目(本地C ++)的编译和链接时间?

请说明每个建议是针对调试,发布还是两者都适用。


4
他在等待链接完成时说:“我希望这是两个问题,一个用于链接,一个用于编译。” - David Norman
1
可能有些答案同样适用于两种情况。 - Brian R. Bondy
你可以在这个帖子中阅读我的文章。这里 - Kaleidos
12个回答

48

尽可能使用前向声明,即使需要写出类型所在的长名称空间。

// Forward declaration stuff
namespace plotter { namespace logic { class Plotter; } }

// Real stuff
namespace plotter {
    namespace samples {
        class Window {
            logic::Plotter * mPlotter;
            // ...
        };
    }
}

它大幅减少了编译时间,也适用于其他编译器。确实,它适用于所有配置 :)


1
相应的,也要尽可能地将头文件中的包含移动到实现文件中。 - Boojum
ceretullis,是的,我认为这就是他的意思。将前向声明放入头文件中,然后将所需的头文件包含到实现文件中。 - Johannes Schaub - litb
@ceretullis:我不明白你的观点。如果由于某些实现细节需要一些头文件,而其中定义的内容(类/函数)不参与接口,则应将该头文件移动到实现文件中。否则,您将增加耦合和依赖关系。 - David Rodríguez - dribeas
1
你是不是指将前置声明本身放入单独的头文件中,就像<iosfwd>标准头文件一样?我对此不太确定,只有在我看来仅有限的一组前置声明才有意义。 - Johannes Schaub - litb
2
像 <iosfwd> 一样。我每个库都使用一个这样的文件(通常与每个“顶级”命名空间相同),发现拥有不需要的前向声明并不会产生任何成本。这使得下一个开发人员可以轻松地包含一两个前向声明文件并获取他们所需的所有内容。 - Zero
显示剩余3条评论

21

使用句柄/体模式(有时也称为“pimpl”,“适配器”,“装饰器”,“桥接”或“包装器”)。通过将类的实现隔离到您的.cpp文件中,它们只需要编译一次。大多数更改不需要更改头文件,因此这意味着您可以进行相当广泛的更改,而只需要重新编译一个文件。这还鼓励重构和编写注释和单元测试,因为编译时间减少了。此外,您自动分离了接口和实现的问题,因此代码的接口得到简化。


1
这些建议中的大多数都会降低代码的可读性和可维护性...因此,如果您只是为了加速编译而实现pimpl习惯用法,请记住权衡。 - oz10
6
使用这种模式极大地提高了代码的可读性和可维护性,原因我已经解释过了。 - 1800 INFORMATION

14

如果您的大型复杂标头文件需要在构建过程中的大多数.cpp文件中包含,并且很少更改,您可以预编译它们。在具有典型配置的 Visual C++ 项目中,这只是将它们包含在 stdafx.h 中的简单问题。该功能有其批评者,但是充分利用模板的库往往在标头中有很多内容,而预编译标头是在这种情况下加速构建的最简单方法。


PCH文件在加速大型构建方面非常出色,但如何设置它们并不总是那么明显 - 我之前写过一篇关于此的答案:https://dev59.com/Wmgt5IYBdhLWcg3w7BkE#11724752 - the_mandrill

8

这些解决方案适用于调试和发布,重点是针对已经很大且臃肿的代码库。

前置声明是一个常见的解决方案。

分布式构建,例如使用Incredibuild是一种胜利之举。

从头文件中将代码推入到源文件中可能会起作用。小型类、常量、枚举等可能最初出现在头文件中,仅仅因为它们本来可以被用于多个编译单元,但实际上只在一个单元中使用,它们可以被移动到cpp文件中。

我使用过的一种解决方案是拆分大型头文件。如果您有一些非常大的头文件,请查看它们。它们可能包含相关信息,并且还可能依赖许多其他头文件。获取没有依赖其他文件的元素...简单结构体、常量、枚举和前置声明,并将它们从the_world.h移动到the_world_defs.h。现在您可能会发现,很多源文件现在可以仅包含the_world_defs.h而避免包含所有那些开销。

Visual Studio还具有“显示包含文件”的选项,可以让您了解哪些源文件包含了许多头文件,哪些头文件最常被包含。

对于非常常见的包含文件,请考虑将它们放入预编译头文件中。


你能描述一些将枚举从头文件移动到源文件的技巧吗?这是我在该主题上的问题链接。 - Grim Fandango

8

一个真正有效的链接:http://altdevblog.com/2011/08/14/the-evils-of-unity-builds/ - Reunanen

7

@Gelldur修复了链接。 - crashmstr
1
好吧,链接时间仍然没有减少 :) - Viktor Sehr

7
编译速度的问题非常有趣,Stroustrup在他的FAQ中也提到了这个问题。

5

4

我们的开发机器都是四核的,而且我们使用支持并行编译的Visual Studio 2008。我不确定所有版本的VS都能做到这一点。

我们有一个包含大约168个单独项目的解决方案文件,在我们的四核机器上编译需要大约25分钟,而在我们提供给暑期学生的单核笔记本电脑上需要大约90分钟。虽然这些机器不完全可比,但你可以理解其中的差别 :)


1
这是未记录的,但同样的标志(/MP)也可以与VS 2005一起使用。 - Luca Tettamanti
1
由于与增量构建不兼容,/MP在调试中无法正常工作。 - Brian R. Bondy
1
很遗憾,链接步骤似乎不是并行的。 - David Thornley

2
使用Visual C++,有一种被称为Unity的方法,可以通过减少目标模块数量来大大提高链接时间。这涉及将C++代码连接起来,通常按库组进行。当然,这会使编辑代码变得更加困难,并且如果没有很好地使用命名空间,您将遇到命名空间冲突。它阻止您使用“using namespace foo”。我们公司的几个团队都有精心设计的系统,在编译时将普通的C++文件连接在一起作为构建步骤。链接时间的缩短可能是巨大的。

1
以编译时间为代价:如果您只更改库中的一行代码,则必须完全重新编译它。 - David Rodríguez - dribeas
1
但是,通过将代码分组为2个一组进行连接,最坏情况下的构建时间几乎减少了一半,并且最好的非平凡情况下的构建时间仅增加了几个百分点,因为所有编译时间都花费在头文件中的代码上,而不是cpp文件中的代码。因此,选择2 <= n <= everything。 - Steve Jessop

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接