减少 C++ 编译时间的实用窍门

减少 C++ 编译时间的实用窍门_第1张图片

编译时间是个大问题,对吗?

某位不愿意透露姓名的大型跨国客户曾告诉我他的一个困扰。他们有一个 Perl 脚本,用于构建 C++ 医疗成像平台。这个脚本名字叫“medmake”,但工程师称之为“mad make”,因为这个脚本耗费的时间无穷无尽。?

我还知道有一家小型航运公司,因为没有人能维护他们定制的构建系统,最终放弃了这个用 C 语言设计的优秀软件。而且其中一个联合创始人认为,他可以做得更好。真是个悲剧!

C++ 编译需要多长时间才能完成?冗长的 C++ 编译时间,是否让你对低效的生产率感到绝望?为此,我特地撰写了本篇博客。如果你想了解更多的细节,建议查看这个主题的相关指南。

但是……加速编译不是你的职责所在吗?

的确是,但我们在不同领域各有所长。如果你试过我们的解决方案,你将明白为什么在加速编译领域,Incredibuild 是当之无愧的市场领导者。这个指南凝聚了我们多年 C++ 工作的精髓,也可以帮助大家减轻很多工作的痛苦。

为什么 C++ 构建耗时这么久?

C++ 是一种复杂的语言,它的句法解析就像试图理解“Time flies like an arrow; fruit flies like a banana”这句话一样困难(究竟是“时间像箭一样飞逝,水果像香蕉一样飞逝”还是“时间像箭一样飞逝,果蝇喜欢香蕉”?两种理解在语法上都是正确的)。Scott Meyers,C++ 的权威,创造了“最让人费解的解析”这个术语,更多内容可以点击链接阅读。在我看来,在 C++ 中,“让人费解的解析”比比皆是。编译器优化,可能是一剂特效药,构建说不定马上就能完成……你也会马上兴奋起来。

接下来将推荐一些实用的操作:

  • 解除资源限制。构建可能受限于 CPU 资源(多个编译并行进行)、受限于网络资源(需要从存储库获取构建工件),或受限于磁盘资源(构建工件需要保存到本地计算机)。我们需要识别特定情况下的资源限制,比如网络从 100Mbps 升级到 1Gbps,将 C++ 编译时间明显大幅缩短。
  • 减少不必要的依赖项。依赖反转是大家都清楚的实用原则。但为了减少构建时间,你需要尝试去消除依赖项。是不是有一些编译单元不需要的头文件?或者更糟糕的是,冗余的头文件中是否还包含头文件?是时候去改变这些情况了,使用工具,例如 include-what-you-use 删除代码库中的依赖项。推荐阅读博客《如何高效创建 C++并行构建?》,了解如何使用这个优秀的工具。

减少 C++ 编译时间的实用窍门_第2张图片

  • 使用更先进的编译器。编译器一直在进步,优化和生成优质代码效率越来越高,编译时间也相应减少。升级编译器。势必会提高 C++ 编译时间。
  • 了解配置管理系统及其功能。假设你使用 IBM ClearCase 作为配置管理系统。对于构建而言,获取快照视图总是比使用动态视图更有效。使用 MultiSite VOBs,让 VOB更近一步,提高构建性能。
  • 使用预编译头文件。谁不喜欢预编译头文件?概念简单直接。较少修改的头文件保存在中间,以加快构建。在本地编译时,这会明显加快构建速度,且没有任何不良影响。但在分布式构建中,预编译头文件无法并行构建多个编译单元,反而会聚合这些单元,从而降低了效率。所以这是要注意的地方。
  • 使用 pImpl idiom。它也被称为“编译时防火墙”,这个名称有其原因。将一个类的执行细节隐藏在另一个不向客户机公开的类中,这是减少耦合的一个有效方法。但是在性能上有所牺牲,因为有一个指向实际执行的间接指引。如果你的非功能性质量属性显示可以使用pImpl,那么一定要去尝试。这个指南更加详细地介绍了这部分内容。
  • 使用文件保护符。任何时候,我们都需要把文件放在文件保护符下。如果你无法为文件保护符找到合适的名称,请使用 #pragma(如可用的话),并将该工作委托给编译器。
  • 使用更好的链接器。就像编译器一样,链接器也能加速工作完成。在本地支持的 GNU binutils 链接器中,gold 比 bfd 更快。使用 llvm linker lld 的速度明显快于gold。当然,gold 比 lld 兼容性更强。因此,大家需要明智地选择。
  • 花时间维护代码。删除未使用的代码,有可能是编译器标记为不可访问的代码块,或者功能删除很久且仅用于测试的代码文件。当编译器负担减轻时,C++编译时间自然也缩短了。
  • 警惕自动生成的代码。有些程序会根据输入创建 C++ 的源代码,外部领域特定语言(DSL)就是一个很好的例子。DSL 可以是文本,并转换为 C++,然后编译,但这种转换可能会出现问题。因此,我们需要检查生成的代码,看看是否可以对其进行优化。
  • 尽可能使用交叉编译。不要坚持在本地使用 Raspberry Pi 制作 Qt5,这需要一整夜的时间。还有更好的方法可以让桌面机器 Raspberry Pi 交叉编译 Qt 吗? 如果可以使用 Dockerfile,为什么要坚持本地构建?使用Docker容器,让构建更快乐。
  • Apply the YAGNI principle.
  • 应用 YAGNI 原则。

如果你现在不需要,就不要过载,不用为以后做准备。如果没必要,也不要将函数或类模板化。保持简单,越少越好,构建也能更快。

有哪些 C++ 编译禁忌?

是的,我们讨论了一些实用的方法来减少构建时间。现在,我们来看看加速构建中应该避免哪些操作。

  • 不要中断编译器优化。不要这样做,因为设计编译器的人最了解状况,他们的优化肯定能让代码更快、更小。禁用优化去节省编译时间,这是得不偿失的。
  • 不要删除代码中的注释。让我惊讶的是,大家居然以为删除代码注释就能加快编译速度。代码文档的存在是有原因的,不要为了节省编译时间而删除。
  • 不要放弃减少编译时间的最佳操作。是的,一般情况下,自动(AAA)模式是最好的方式(从 C++17 开始,就一直是自动模式)。为了争分夺秒进行构建,不要返回显式键入,不值得。
  • 统一汇编单位。我强烈建议不要统一构建,这是后期维护的噩梦。我们已经步入 2021 年,模块化才是趋势,后面的开发人员也会感激你前期的模块建设。
  • 不要随便切换编程语言。你听说过 Golang 的编译速度很快。Rust 呢?

减少 C++ 编译时间的实用窍门_第3张图片

  • 不要因为编译时间,放弃 C++ 而使用其他语言。我们有更好的方法!

总结

这篇文章,以及之前的指南,都能帮助减少 C++ 构建时间。然而,大多数技术方案都需要付出很多努力,而且并不一定能马上看到成效。如果你需要实现爆发式增速,且不改变使用的代码和工具,那你一定不要错过我们的解决方案,让编译时间大幅缩减!

点击获取试用 License !
减少 C++ 编译时间的实用窍门_第4张图片

你可能感兴趣的:(C++,c++)