比忍者更快
发表于2026年8月5日,作者:Boris Kolpackov 几个月前,我读到一篇关于用于窥探慢构建系统的工具的文章,该工具可用于查找构建瓶颈。尽管整篇文章相当有启发性,但这段话让我印象深刻:忍者并不是与其他工具的100%公平比较,因为它受益于创建忍者文件的工具的一些“硬编码”构建逻辑,但我认为这是一个合理的构建系统“光速”性能基准。为了澄清,作者所说的“硬编码”是指没有人手动编写忍者构建文件。相反,第二个工具,如CMake,会被调用来生成它们,并且在此生成阶段执行的一些步骤(如下所示示例)理想情况下应该是构建阶段的一部分。此外,忍者以极简主义著称,提供的功能仅为基本的最低限度,尤其是在更改跟踪方面(同样如下所示示例)。现代构建系统预计会提供更多功能。尽管如此,看看现代的本地(即,没有生成步骤)构建系统能在多大程度上接近“光速”仍然是很有趣的。让我们看看build2的表现。虽然有一些重要项目(如Boost和Qt)可以在这两种构建系统上构建,但要找到一个可以进行真正的苹果对苹果比较的实质项目是困难的,因为当我们为build2打包更复杂的项目时,我们必须不可避免地将“内部依赖关系的大球”结构理顺成更有序的东西(一个好的例子,请查看上游qtbase模块与build2包的对比)。这通常导致一组稍微不同的中间构建工件,如引导程序和实用程序库。因此,我们必须使用一些更简单的实例,在这些实例中,我们可以确保生成的对象文件和二进制文件的集合基本上是通过相同的编译和链接选项生成的。最终,我选择了Xerces-C++,一个用于C++的XML解析器/序列化程序。它具有相当多的功能(如XML Schema验证),因此并不算小,包含299个C++翻译单元,链接成一个共享库。我们将测试一次,从头开始构建,与引用的文章中的构建相同。忍者在我的计算机上完成该构建(见下面的基准详细信息)耗时3.4秒:时间(平均值±σ):3.429秒±0.029秒 [用户:48.536秒,系统:5.033秒] 范围(最小…最大):3.383秒…3.464秒 10次运行 在测量build2之前,至少我们要承认一个显而易见的问题:虽然忍者在3.4秒内构建了该项目,但CMake需要15.6秒来生成忍者构建文件。因此,如果您将Xerces-C++作为项目的依赖项并从头开始构建,您将需要等待19秒,而不是3.4秒,才能完成构建。采用匹配配置(相同的C++编译器、C++标准、调试构建等),build2耗时3.8秒,约慢11%:时间(平均值±σ):3.808秒±0.046秒 [用户:58.037秒,系统:8.012秒] 范围(最小…最大):3.746秒…3.886秒 10次运行 非常接近,但未达到光速。让我们看看能否做到。也许在真空中构建会有所帮助?为了更接近忍者的时间,我们将使比较更为准确,真正做到苹果对苹果。正如前面所讨论的,忍者以极简主义著称,而build2则提供了忍者所没有的许多功能。其中一些功能在性能上具有可测量的成本。因此,我们将在某些特性上进行禁用,以更接近忍者的工作量。我们要禁用的第一个功能是对C和C++源文件的更精确的更改跟踪。忍者仅检查文件的修改时间是否发生变化,如果发生变化,则重新编译它。相比之下,build2在这种情况下会执行额外的步骤:它对(部分预处理的)源文件进行标记化,并计算生成的标记的校验和。如果自上次编译该文件以来,这个校验和没有变化,那么它就跳过重编译。这忽略了仅更改空格的情况(只要这些更改不改变标记的列号),在开发过程中非常有用(在某些情况下至关重要,例如,如果您希望在每次提交时更改项目的版本)。但在从头开始构建中,对299个翻译单元进行标记化的前期成本是不可忽视的,尽管这可能在后续增量构建中得到回报。禁用这种可忽略的更改检测的方法是告诉build2该项目是只读的(对于外部依赖项,包管理器会自动完成此操作)。在这种情况下,build2将回退到仅使用修改时间,与忍者相同。做出此更改后,我们的构建时间降至3.4秒,几乎与忍者一样:时间(平均值±σ):3.433秒±0.055秒 [用户:51.093秒,系统:6.701秒] 范围(最小…最大):3.383秒…3.551秒 10次运行 让我们看看是否还能更快。接下来,我们禁用文件缓存中的压缩。我们稍后将更详细地讨论文件缓存,但现在只需说,通过禁用压缩,可以进一步缩短构建时间。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡