C++ 通过使用库提高大型项目的构建速度
Posted
技术标签:
【中文标题】C++ 通过使用库提高大型项目的构建速度【英文标题】:C++ increase build speed in large project by using libraries 【发布时间】:2017-10-04 07:01:06 【问题描述】:我目前正在尝试优化一个大型项目的构建速度,并考虑到以下几点:
构建速度是第一要务 生成的二进制文件大小不重要信息:
环境:Visual Studio 2012(必需,因为我正在开发的软件)+ Windows 机器 构建时间:12 分钟(干净构建),1 分钟进行小更改,并且由于链接缓慢(这就是我要解决的问题),每一次的小更改都会导致 5-6 分钟。 项目中的自定义文件:大约。 2500(我需要使用的SDK除外,CAD系统的大SDK) 自定义文件中的代码行数:大约。 500000 我正在使用支持 CAD 的最新计算机(32GB RAM、>3GHz QuadCore、SSD)想法:
使用预编译头文件 => 完成,但没有我想要的效果;在大多数情况下有助于加快编译时间,但时不时地并没有这样做 将项目拆分为库 => 不确定这是否有帮助问题
我找不到任何关于使用库和构建速度的信息,但我认为如果我预编译库,链接器会更快。
这个假设是真的吗? 如果我创建一个包含核心功能的静态库,这会影响构建时间吗?或者链接器是否需要与当前一样长? 如果我创建一个动态库,这会影响构建时间吗?还是链接器会再次完全检查 dll 并需要相同的时间?【问题讨论】:
它是特定于操作系统的(Windows DLL 与 Linux 上的共享库非常不同)。考虑使用一个好的build automation 工具,例如GNU make 或ninja。显示您的编译命令(或您的Makefile
)。详细介绍您的项目(有几百万行,它在做什么,它正在使用哪些库)
添加了我的构建环境,我坚持使用旧的 VS 版本和 Windows 计算机
最好放一个 Windows 标签。 AFAIK Windows 没有共享库,而是 DLL(带有奇怪的链接方案)。你应该更多地了解你的项目(有多少源文件,多少百万行源代码)。 AFAIK make
也用于 Windows,您可以从它们的源代码构建 GNU make
或 ninja
并将它们与 Visual Studio 编译器一起使用,因为它们都是免费软件。
如果没有更多细节,这个问题仍然不清楚且过于宽泛。你可以阅读莱文的linkers and loaders 书;但我强烈建议使用良好的外部构建自动化工具(例如从其源代码编译的ninja
)
添加了一些一般信息...不知道我还应该添加什么。这实际上是一个普遍的问题
【参考方案1】:
我假设如果我预编译库,链接器会更快。这个假设是真的吗?
不,不太可能。如果有的话(因为链接器必须打开更少的文件),那么差异将是微不足道的。
如果我创建一个包含核心功能的静态库,这会影响构建时间吗?还是链接器需要和当前一样长?
这可能会在编译时间产生巨大的差异,因为尽管在真正干净的重建中您仍然必须像以前一样编译所有内容,但在正常的“大部分干净”重建中重建支持库是多余的因为它们内部没有任何变化,所以您真正需要重建的只是用户代码,因此您编译的文件要少得多。 请注意,每个健全的构建系统通常都会构建一个依赖关系图并尝试编译尽可能少的文件(并且在可能的范围内,具有某种程度的并行性),除非您明确告诉它这样做一个干净的构建(很少需要这样做)。医生,我这样做会很痛——好吧,不要这样做。
链接器的差异也将是微不足道的。链接器仍然需要查找完全相同数量的符号,并且仍然需要将相同数量的代码复制到可执行文件中。
您可能想使用链接顺序。听起来很有趣,有时链接库和目标文件的顺序会对链接器完成其工作所需的时间产生 5 倍的差异。
话虽如此,12 分钟的干净构建确实不是很多。您的非干净构建可能会在两位数的秒范围内,其中链接可能需要 90%。这通常不是一个炫耀。当构建需要 4 小时时回来 :-)
如果我创建一个动态库,这会影响构建时间吗?还是链接器会再次完全检查 dll 并需要相同的时间?
链接器仍然需要为您调用的每个函数做一些工作,这可能会稍微快一点,但仍然或多或少相同。 p>
请注意,通过将代码移动到 DLL 中会增加运行时(启动)开销。加载程序在 DLL 中加载包含部分代码的程序需要做更多的工作,因为它需要加载另一个图像、解析其标题、解析符号、设置一些指针、运行每个线程的 init 函数等。这通常是不是问题(差异并不那么明显),只是让您知道它不是免费的。
【讨论】:
所以对于编译时优化,使用库(静态或动态)不会产生明显的差异,我所能做的就是优化我的代码(包括、设置等),对吗? 是否构建库或可执行文件对编译产生零差异。在每种情况下,对于每个翻译单元,都会生成一个目标文件。在最终登陆可执行文件之前是否将其放入一个或另一个“容器”中是非常相同的(而不是编译范围)。包含文件和模板确实可以极大地影响编译时间,在不健康的情况下可能会总体上提高 10 倍(但它们确实不影响链接时间,您将其称为问题)。好吧,这只有 99% 是正确的,有#pragma
指令确实会影响链接时间...【参考方案2】:
12 分钟是很短的完整构建时间,而 500KLOC 并没有那么大。许多免费软件项目(GCC、Qt、...)都有更长的项目(小时)和数百万行 C++。
您可能想要使用一个严肃且并行的build automation 工具,例如ninja。如果你可以在远程机器上编译,也许你可以做一些分布式构建(就像distcc 允许的那样)。
您可以将 IDE 配置为运行外部命令(例如 ninja
)进行构建。这不会改变自动完成能力。你可以采用另一个source code editor(例如GNU emacs)。
C++(还)不是模块化的(它没有真正的模块,例如 Ocaml 或 Go),这使得它的编译速度很慢(例如,因为标准容器标头很大,例如 <vector>
带来了大约 10KLOC 的包含代码,可能在您的大多数 C++ 代码中使用并包含)。所以你应该避免有很多小文件(例如,将两个 250 行的文件合并到一个 500 行的文件中可能会减少构建时间),看起来你有太多的小 C++ 文件。我会推荐每个超过一千行的源文件。每个源文件只有一个类实现(或一个函数)会减慢总构建时间。
您肯定希望在代码中使用更多的间接性。更系统地使用PIMPL 成语和virtual method tables,闭包,std::function
-s。记住rule of five。
【讨论】:
以上是关于C++ 通过使用库提高大型项目的构建速度的主要内容,如果未能解决你的问题,请参考以下文章
如何在 WSL2 上使用 OpenCV 加快 C++ 项目的构建速度? [关闭]