如何在VS2005中提高大型C ++应用程序的链接性能
我们有一个相当大的C ++应用程序,在Visual Studio 2005中它由大约60个项目组成。在"发布"模式下,链接需要7分钟,我想尝试减少时间。有什么技巧可以改善链接时间吗?
大多数项目都编译为静态库,这使测试更加容易,因为每个项目也都有一组关联的单元测试。似乎使用静态库会阻止VS2005使用增量链接,因此即使启用了增量链接,VS2005也会每次都执行完整链接。
将DLL用于子项目会有所不同吗?我真的不想遍历所有标头并添加宏以导出符号(即使使用脚本),但是如果这样做可以减少7分钟的链接时间,我肯定会考虑的。
出于某些原因,从命令行使用nmake会稍微快一些,而在Linux上使用GCC链接相同的应用程序会更快。
- Visual Studio IDE 7分钟
- 从命令行使用nmake的Visual C ++-5分钟
- Linux上的GCC 34秒
解决方案
链接的60个库听起来确实很少。这可能是一个极端的措施,但可能会从根本上加快速度。创建一个包含几个项目的新解决方案,然后将现有项目中的所有源添加到这些项目中。然后构建并链接它们,只保留较小的以进行测试。
获得一台具有多个处理器的更快的计算机,并启用并行构建(默认情况下可能处于启用状态)。为了最大程度地提高并行度,请确保项目依赖项是正确的,并且没有不必要的依赖项。
我认为转换为DLL不会有用。
我们可以尝试寻找与优化有关的选项,然后将其关闭。链接器可能会花费大量时间在库中寻找可以消除的冗余代码。应用最终可能会变大或者变慢,但这对我们来说可能不是问题。
通常,使用DLL代替静态库会大大缩短链接时间。
看看Xoreax的Incredibuild。它的分布式编译将我们的完整构建/链接时间从大约40分钟减少到了8分钟。
此外,该产品具有一个称为Incredilink的功能,该功能即使在静态链接的库中也应能使增量链接正常工作。
有几个人报告(我本人也注意到),在静态链接库中修改文件将禁用整个解决方案的增量链接。这似乎就是我们所看到的。有关此方面的一些信息,请参见此处和此处的评论。
一种解决方法是使用快速解决方案构建外接程序。这可能需要对工作空间进行一些更改,但是绝对值得。对于商业解决方案,请使用Xoreax的Incredibuild,它基本上采用了相同的技术,但同时还增加了其他功能。如果我听起来像Incredibuild的推销员,我深表歉意,我只是一个非常满意的客户。
如果我们真正谈论的是链接时间,那么快速解决方案构建和Xoreax之类的东西并没有多大帮助(可能是Incredilink除外)。假设我们真正测量的是链接的开始到链接的结束,那么我建议我们使用的库数是个问题。
链接阶段至少在最初是绑定IO来加载所有对象和lib文件的。我们可能会遇到这样的情况:我们拥有60个库以及一些大量.obj文件的主项目。我怀疑我们可能至少部分地看到典型的Windows加载所有这些lib和.obj文件的速度缓慢。
我们可以轻松地对此进行测试。以所有这些lib文件为例,并构建一个单独的lib文件作为测试。与其链接60个,而不是链接60个,然后看看时间在哪里。那会很有趣。
NTFS非常慢。在Linux上,它的播放时间不应少于7秒,而32秒应该是32秒,但这可能是问题的一部分。使用DLL会有所帮助,但是我们会遭受应用程序启动时间的困扰,尽管这不会太早。我相信我们不会有700万个应用程序启动时间。
以前,在将大型应用程序与Visual C ++链接时,我也遇到过类似的麻烦。就我而言,我根本没有足够的可用RAM,并且过多的磁盘分页使链接过程停顿了下来。将我的RAM从1GB增加到2GB带来了巨大的进步。开发箱运行多少钱?
我们可以尝试看一下:http://msdn.microsoft.com/en-us/library/9h3z1a69.aspx
基本上,如果我们有多个核心,则可以并行运行项目构建。
如果我们使用/ GL
标志启用整个程序优化(WPO)或者/ LTCG
标志启用链接时间代码生成,则将其关闭将大大缩短链接时间,但会牺牲一些优化的代价。
同样,如果我们使用/ Z7
标志将调试符号放在.obj
文件中,则静态库可能很大。如果使用" / Zi"创建单独的" .pdb"文件,则可能会有所帮助,如果它阻止链接器从磁盘读取所有调试符号。我不确定它是否真的有帮助,因为我还没有对其进行基准测试。
我刚刚发现,我们偶然在头文件中定义了一个很大的字符串表,该表几乎包含在每个(静态)库中。 (我说的是一个巨大的C ++项目。)当链接器创建EXE时,它看起来像表的统一(在EXE中只有一个结尾)或者对库的解析要花很长时间。将表放在单独的C ++文件中,在相对较慢的计算机上花费了几分钟的链接时间。
不幸的是,除了偶然之外,我不知道如何找到类似的东西。
对于调试版本,可以使用增量链接,这可以大大缩短链接时间。
令人遗憾的是,这里有某些陷阱,VS2005不会警告我们。
- 如果使用静态库,则在修改静态库的文件部分时增量链接将不起作用。解决方案是将链接器选项"使用库依赖项输入"设置为"是"(与VS2003中的快速解决方案构建相同)
- 如果使用pragma-comment-lib包含DLL的lib,并指定相对路径而不是单独的lib,则增量链接将停止工作。解决方案是仅指定库,然后使用链接器选项LIBPATH添加其他库路径。
- 有时.ilk文件将损坏(增长到200 MB以上),然后突然之间,增量链接器所需的时间超过正常时间的10倍。有时,它会抱怨.ilk文件已损坏,但通常是几分钟后才开始。对我来说,解决方案是为($(IntDir)*。ilk)中的%% f设置"构建事件"->"预链接事件"的以下命令(如果" %%〜zf" GTR" 200000000 "(删除%% f))
请参阅我在Microsoft提出的建议:https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=511300
我们应该投票!这是我对此的最后评论:
是的,我们正在使用增量链接来构建大多数项目。对于最大的项目,它没有用。实际上,使用增量链接来链接那些项目要花费更多的时间(2min50比2min44)。我们观察到,当ILK文件很大时,它不起作用(我们最大的项目在win 32中生成了262144 KB的文件)。
在下面,我列出了我们尝试减少链接时间的其他事项:
- 显式模板实例化以减少代码膨胀。小收益。
- IncrediLink(IncrediBuild为编译带来了有趣的收益,而链接却几乎没有收益)。
- 删除调试很少的库的调试信息(增益很高)。
- 在"预构建事件"中删除PDB文件(奇怪的是,它给出了有趣的增益,例如:2min44而不是3min34)。
- 将许多静态库转换为DLL。重要的收获。
- 与配备大量RAM的计算机一起使用,以最大程度地提高磁盘缓存。最大的收获。
- 大obj与小obj。没有不同。
- 更改项目选项(/ Ob1,/ INCREMENTAL,启用COMDAT折叠,嵌入清单等)。有些给人带来有趣的收获,而其他却没有。我们尝试不断地最大化我们的设置。
- 最大化内部联系与外部联系。这是一个很好的编程习惯。
- 尽可能多地分离软件组件。然后,我们可以快速进行链接的单元测试。但是,我们仍然必须将事物整合在一起,我们拥有旧版代码,并且我们使用了第三方组件。
- 使用秘密链接程序开关/ expectedoutputsize:120000000。小收益。
请注意,对于我们所有的实验,我们都精心测量了链接时间。缓慢的链接时间会严重影响生产力。当我们实现复杂的算法或者跟踪棘手的错误时,我们想要快速迭代此序列:修改一些代码,链接,跟踪调试,修改一些代码,链接等。
优化链接时间的另一点是它对我们持续集成周期的影响。我们有许多共享通用代码的应用程序,并且我们正在其上进行持续集成。我们所有应用程序的链接时间花费了一半的周期时间(15分钟)...
在线程https://blogs.msdn.microsoft.com/vcblog/2009/09/10/linker-throughput/中,提出了一些有趣的建议来缩短链接时间。在64位计算机上,为什么不提供完全在RAM中处理文件的选项?
同样,欢迎提出任何可以帮助我们减少链接时间的建议。