您使用了哪些策略来改善大型项目的构建时间?

时间:2009-07-02 09:26:09

标签: c++ compiler-construction build-process build-automation

我曾经参与过一个C ++项目,花了大约一个半小时进行完全重建。小编辑,构建,测试周期大约需要5到10分钟。这是一场没有生产力的噩梦。

您需要处理的最差构建时间是多少?

您使用了哪些策略来改善大型项目的构建时间?

更新

您认为使用的语言对这个问题有多大责任?我认为C ++很容易对大型项目产生大量依赖,这通常意味着即使对源代码进行简单的更改也可能导致大规模的重建。您认为哪种语言能够最好地应对大型项目依赖性问题?

20 个答案:

答案 0 :(得分:43)

  1. 转发声明
  2. pimpl idiom
  3. 预编译标题
  4. 并行编译(例如Visual Studio的MPCL加载项)。
  5. 分布式编译(例如,Visual Studio的Incredibuild)。
  6. 增量构建
  7. 拆分构建在几个“项目”中,因此如果不需要则不编译所有代码。
  8. [稍后编辑] 8.购买更快的机器。

答案 1 :(得分:15)

我的策略非常简单 - 我不做大型项目。现代计算的整个主旨是远离巨型和整体式,而不是小型和组件式。因此,当我处理项目时,我会将其分解为可以独立构建和测试的库和其他组件,并且彼此之间的依赖性最小。在这种环境中“完全构建”从未实际发生过,所以没有问题。

答案 2 :(得分:7)

有时有用的一个技巧是将所有内容都包含在一个.cpp文件中。由于包含每个文件处理一次,因此可以节省大量时间。 (这样做的缺点是它使编译器无法并行化编译)

你应该能够指定多个.cpp文件应该并行编译(-j with make on linux,/ MP on MSVC-MSVC也可以选择并行编译多个项目。这些是单独的选项,并且你不应该同时使用这两种方式

同样,分布式构建(例如Incredibuild)可以帮助减轻单个系统的负载。

SSD磁盘应该是一个巨大的胜利,虽然我自己没有测试过(但是C ++版本会触及大量文件,这很快就会成为瓶颈)。

预先编译的标题在使用时也可以提供帮助。 (如果他们不得不经常重新编译,他们也会伤害你。)

最后,尝试最小化代码本身的依赖性非常重要。使用pImpl惯用法,使用前向声明,尽可能保持代码模块化。在某些情况下,使用模板可以帮助您解耦类并最小化依赖性。 (在其他情况下,模板可以显着减慢编译速度,当然)

但是,是的,你是对的,这是一种非常语言的东西。我不知道另一种语言在这个程度上遇到了这个问题。大多数语言都有一个模块系统,允许它们消除头文件,这是一个巨大的因素。 C有头文件,但是这样一种简单的语言,编译时间仍然可以管理。 C ++是两个世界中最糟糕的。一种复杂的大型语言,以及一种可怕的原始构建机制,需要一次又一次地解析大量代码。

答案 3 :(得分:4)

答案 4 :(得分:4)

  1. 多核编译。非常快,在I7上编译了8个核心。
  2. 增量链接
  3. 外部常量
  4. 删除了C ++类的内联方法。
  5. 最后两个让我们减少了从大约12分钟到1-2分钟的连接时间。请注意,只有当事物具有巨大的可见性时才需要这样做,即看到“无处不在”并且有许多不同的常量和类。

    干杯

答案 5 :(得分:3)

Unity Builds

Incredibuild

指向实施的指针

转发声明

将项目的“已完成”部分编译为dll的

答案 6 :(得分:3)

最好的建议是构建实际理解依赖关系的makefile,而不是为了一个小的改变而自动重建世界。但是,如果一个完整的重建需要90分钟,而一个小的重建需要5-10分钟,那么你的构建系统已经做到这一点的几率很高。

构建是否可以并行完成?有多个核心,还是有多个服务器?

检查预先编译的位,用于真正静态的片段,不需要每次都重建。使用但未改变的第三方工具/库是这种治疗的良好候选者。

如果适用,将构建限制为单个“流”。 “完整产品”可能包括调试版本,或32位和64位版本,或者可能包含每次派生/构建的帮助文件或手册页。删除开发不必要的组件可以大大减少构建时间。

构建是否也包装产品?这真的是开发和测试所必需的吗?构建是否包含一些可以跳过的基本健全性测试?

最后,您可以将代码库重新分解为更加模块化并具有更少的依赖性。 Large Scale C++ Software Design是学习将大型软件产品分解为易于维护和更快构建的产品的绝佳参考。

编辑:构建在本地文件系统而不是NFS挂载的文件系统上,也可以大大加快构建时间。

答案 7 :(得分:3)

ccache& distcc(用于C / C ++项目) -

ccache缓存已编译的输出,使用预处理的文件作为查找输出的“键”。这很好,因为预处理非常快,并且经常更改强制重新编译实际上不会更改许多文件的源。而且,它确实加速了完整的重新编译。同样不错的是您可以在团队成员之间拥有共享缓存的实例。这意味着只有第一个获取最新代码的人才会编译任何内容。

distcc通过网络机器进行分布式编译。只有拥有用于编译的机器网络时才有用。它适用于ccache,并且仅移动预处理源,因此在编译器引擎系统上唯一需要担心的是它们具有正确的编译器(不需要标头或整个源树可见)。

答案 8 :(得分:2)

  1. 摆弄编译器优化标志,
  2. 为gmake使用选项-j4进行并行编译(多核或单核)
  3. 如果您使用clearmake,请使用闪烁
  4. 我们可以在极端情况下取出调试标志。
  5. 使用一些功能强大的服务器。

答案 9 :(得分:2)

这本书Large-Scale C++ Software Design在过去的项目中有很好的建议。

答案 10 :(得分:2)

  1. 最小化您的公开API
  2. 最小化API中的内联函数。 (不幸的是,这也增加了链接器的要求)。
  3. 最大化前向声明。
  4. 减少代码之间的耦合。例如,将两个整数传递给一个函数,用于坐标,而不是自定义的Point类,它有自己的头文件。
  5. 使用Incredibuild。但它有时会出现一些问题。
  6. 不要将从两个不同模块导出的代码放在SAME头文件中。
  7. 使用PImple惯用法。之前提到过,但有些重复。
  8. 使用预编译的标头。
  9. 避免使用C ++ / CLI(即托管c ++)。链接器时间也受到影响。
  10. 避免使用包含API中“其他所有内容”的全局标头文件。
  11. 如果您的代码并不真正需要,请不要依赖于lib文件。
  12. 了解包含带引号和尖括号的文件之间的区别。

答案 11 :(得分:1)

这是我们在Linux下为开发所做的事情列表:

  • 正如Warrior所说,使用并行构建(make -jN)
  • 我们使用分布式构建(目前icecream,这很容易设置),我们可以在给定时间内拥有数十个或处理器。这也有一个优点,即可以为最强大,负载较少的机器提供构建。
  • 我们使用ccache,这样当您进行make clean时,您不必真正重新编译未更改的源,而是从缓存中复制它。
  • 另请注意,编译通常编译速度更快,因为编译器不必进行优化。

答案 12 :(得分:1)

在Visual Studio中,您可以设置一次编译的项目数。它的默认值是2,增加会减少一些时间。

如果您不想弄乱代码,这将有所帮助。

答案 13 :(得分:1)

我们尝试过创建一次代理类。

这些实际上是仅包含公共接口的类的简化版本,减少了需要在头文件中公开的内部依赖项的数量。然而,他们付出了沉重的代价,将每个类分散在几个文件上,这些文件都需要在更改类接口时进行更新。

答案 14 :(得分:1)

强大的编译机器和并行编译器。我们还确保尽可能少地完成整个构建。我们不会更改代码以使其编译更快。

效率和正确性比编译速度更重要。

答案 15 :(得分:1)

一般来说,我所做过的大型C ++项目构建时间很慢,但是很多相互依赖性分散在代码中(大多数cpps都使用相同的文件,胖接口而不是瘦接口)。在这些情况下,缓慢的构建时间只是较大问题的症状,并且是一个轻微的症状。重构以使更清晰的接口和将代码分解到库中改进了体系结构以及构建时间。当你创建一个库时,它会强迫你思考什么是界面,什么不是,实际上(根据我的经验)最终会改进代码库。如果没有技术上的理由不得不划分代码,那么一些程序员在维护过程中只会向任何头文件抛出任何内容。

答案 16 :(得分:1)

CătălinPitiş报道了许多好事。我们做的其他事情:

  • 拥有一个工具,可以为在非常大的整体项目的特定子区域中工作的人生成缩减的Visual Studio .sln文件。
  • 从CI构建它们以在开发者计算机上分发时缓存DLL和pdb
  • 对于CI,请确保链接机器具有大量内存和高端驱动器
  • 在源代码管理中存储一些昂贵的重新生成文件,即使它们可以作为构建的一部分创建
  • 使用我们自己的脚本根据我们的情况替换Visual Studio检查需要重新链接的内容

答案 17 :(得分:1)

这是我的一个宠儿,所以即使你已经接受了一个很好的答案,我也会说:

在C ++中,它不是那种语言,而是语言强制的构建模型,它在七十年代很棒,而且是头文件重的库。

关于CătălinPitiş回复的唯一错误:“购买更快的机器”应该先行。这是影响最小的最简单方法。

在W2K Professional上运行VC6的老化构建机器上,最糟糕的是大约80分钟。在具有4个超线程内核,8G RAM Win 7 x64和不错的磁盘的机器上,相同的项目(大量新代码)现在需要不到6分钟。 (类似的机器,处理器功耗减少约10..20%,4G RAM和Vista x86需要两倍的时间)

奇怪的是,增量版本的大部分时间都比现在的完全重建速度慢。

答案 18 :(得分:0)

完整版本大约需要2个小时。我尽量避免修改基类,因为我的工作主要是基于这些基类的实现,我只需要构建小组件(几分钟)。

答案 19 :(得分:0)

创建一些单元测试项目来测试单个库,这样如果您需要编辑可能导致巨大重建的低级类,您可以在重建整个应用程序之前使用TDD知道新代码的工作原理。 Themis提到的John Lakos书中有一些非常实用的建议,可以对你的图书馆进行重组,以实现这一目标。