Gradle 和 Maven:性能比较

加快构建速度的商业案例

Gradle 旨在帮助组织更交付更好的软件。更快的构建是实现这一目标最直接的方式之一;当工程师不必等待软件构建时,他们有更多机会交付软件。CI 实例花费更少的时间进行不必要的重建,从而需要更少的 CPU 资源并更早地提醒工程师。

案例研究

2017 年,我们与一个 600 人的工程师团队合作。平均而言,每位工程师每周运行约 42 次构建。团队估计每位工程师每分钟的成本为 1.42 美元,他们每年至少工作 44 周。

这意味着他们每分钟能让开发人员构建更快,就能为工程师节省 1,600,000 美元的等待时间。

600 名工程师 * 1.42 美元/分钟 * 42 次构建/周 * 44 工作周/年 = 1,600,000 美元/年

如果构建速度快 90% 呢?

性能改进还具有额外的强大经济影响。例如,CI 实例花费更少的时间进行不必要的重建,也需要更少的 CPU 资源,更早地提醒工程师,并构建更小的变更集,这使得调试 CI 失败更容易。

你呢?

我们一次又一次地发现,投资于构建性能的组织通常会很快收回投资,更快地交付,并拥有更快乐、更高效的团队。

更快的构建也有一些不那么明显的优点。在关于速度、性能和人类感知的入门读物中,Ilya Grigorik 认为,心理语境切换可能在几秒钟后就会发生——使用极快构建工具的工程师更有可能保持“心流”状态,从而整体上更具生产力。

Gradle 比 Maven 快 100 倍

我们对常见场景中的各种项目进行了性能测量,以近似 Maven 迁移到 Gradle 的成本摊销。

以下结果是在 MacBook Pro 2018(2.9 GHz Intel Core i9、32 GB RAM、SSD、OSX Mojave (10.14.2))上生成的,使用 10 次运行的平均值。

了解测试项目以及如何使用以下说明自行重现性能结果。

场景:Java 库

为了测量对典型库项目的影响,我们将 Apache Commons Lang 3 项目从 Maven 转换为 Gradle(使用 Java 库插件)。

Gradle 运行测试快 1.7 倍,使用构建缓存时构建快 30 倍!

场景:小型多项目构建

以下是 10 个模块多项目构建(最类似于微服务集合)的常见任务结果。每个子项目有 50 个源文件和 50 个测试源文件。

Gradle 对于干净构建快 2-3 倍,对于增量更改快约 7 倍,当 Gradle 任务输出被缓存时快达 14 倍。

此 GIF 显示了 clean build 场景的并排对比,以便您可以感受其中的差异(不含构建缓存)。

small project dueling builds

场景:中型多项目构建

以下是包含 100 个模块的单存储库多项目构建的常见任务结果。每个子项目有 100 个源文件和 100 个测试文件。

Gradle 对于干净构建快 4-5 倍,对于增量更改快约 40 倍,当 Gradle 任务输出被缓存时快达 13 倍。

场景:大型多项目构建

以下是包含 500 个模块的单存储库多项目构建的常见任务结果。每个子项目有 100 个源文件和 100 个测试文件。

Gradle 对于干净构建快 3-10 倍,对于增量更改快约 85 倍,当 Gradle 任务输出被缓存时快达 13 倍。

场景:大型单体应用

虽然将所有代码放在一个项目中很少见,但多模块构建中,大部分代码存在于一个或少数几个比其他模块大得多的模块中,这种情况却很常见。此场景近似于此类项目——一个包含 50000 个源文件和 50000 个测试文件的单个项目。

Gradle 对于干净构建快 2-3 倍,对于增量更改快约 7 倍,当 Gradle 任务输出被缓存时快达 3 倍。

性能结果总结

  • 在所有结果中,Gradle 在每个场景中至少快 2 倍
  • 当构建增量更改时,Gradle 比 Maven 快 7 到 85 倍;子项目数量越多,效益越大。
  • 当任务输出可以通过 Gradle 的构建缓存解析时,Gradle 构建比 Maven 构建快 3 到 30 倍

与 Maven 相比,实现此目标的性能优势

选择 Gradle,您将受益于与 Apache Maven 相比巨大的性能提升:Gradle 实施了广泛的策略来加速您的构建。

  • Gradle Daemon 是一个长期运行的进程,它将构建信息“热”保存在内存中
  • 各种任务类型的增量任务输入和输出使得不再需要运行 clean
  • 增量编译分析源文件和类之间的依赖关系,并仅重新编译受更改影响的部分
  • 当切换分支或运行干净构建时,如果组织中其他地方已经生成了相同的输出,构建缓存会从缓存中获取结果。
  • Gradle 的智能类路径分析器在库的二进制接口未改变时避免不必要的编译
  • 使用Java 库插件更好地建模依赖关系可以减小编译类路径的大小,这对性能有很大的积极影响

所有这些功能结合在一起,产生了巨大的差异,如上面的性能结果所示。

进一步提升性能

您可能已经注意到,上述测量中,最显著的性能提升来自于利用 Gradle 构建缓存的那些。

Develocity 开箱即用地提供远程构建缓存和方便的管理工具,更不用说用于精美地可视化和比较构建以及查看组织内所有构建的工具了。获得更快、更顺畅的迁移,以便您可以专注于交付软件。通过 Develocity 构建缓存,Gradle 构建工具用户通常会额外缩短平均构建时间 50%。

注意:Gradle 和 Maven 用户都可以利用 Develocity 中提供的构建缓存技术。Gradle 用户通常会额外缩短约 50% 的构建时间,而 Maven 用户通常会缩短约 90%。观看此视频,了解更多关于 Develocity Maven 构建缓存技术和商业案例的信息。

轮到你了

重现结果和方法论

对于我们的测量,我们使用了以下测试项目,对应于实际开发人员经常遇到的四种场景。

  • 10 模块项目,每个模块有 50 个源文件和 50 个测试文件
  • 100 模块项目,每个模块有 100 个源文件和 100 个测试文件
  • 500 模块项目,每个模块有 100 个源文件和 100 个测试文件
  • 大型单体应用,单个项目中有 50000 个源文件和 50000 个测试文件。

您可以通过遵循 GitHub 上发布的说明自行生成这些项目。

为什么干净构建的时间如此重要?

与 Maven 不同,Gradle 可以执行快速、可靠的增量构建。尽管如此,执行干净构建所需的时间仍然是一个相关的指标。它反映了重要的现实世界场景

  • 一个总是使用没有保留本地状态的新从属机器的 CI 构建。
  • 一种总是作为干净构建执行 CI 构建的文化。
  • 开发人员从新的检出中构建
  • 开发人员构建时大量代码发生变化。这并非严格意义上的干净构建。但由于几乎所有内容都需要重新构建,因此干净构建时间最能反映此场景。

对于 Gradle,我们测量 clean assemble,它会清除所有输出,然后编译类、处理资源并构建 jar。它不执行测试。对于 Maven,我们测量 clean package,它做同样的事情,但我们需要显式排除测试执行,使用 -Dmaven.skip.test=true

关于增量构建

典型的开发人员工作流程是编辑一个或几个源文件并重新构建。Maven 的增量编译存在问题(请参阅此 bug另一个此 bug),您不得不总是进行干净构建。相比之下,当只有少量源文件发生更改时,Gradle 构建可靠且快得多(与干净构建相比)。

在 ABI 兼容更改场景中,我们对方法的实现进行更改,这种更改不会改变组件的应用程序二进制接口 (ABI)。

在 ABI 不兼容更改场景中,我们更改了公共方法的公共签名。您可以在我们关于编译避免和增量编译的博客文章中了解更多信息。

关于 Gradle 的构建缓存

Gradle 构建缓存在本地重用 Gradle 任务的输出,并在机器之间共享任务输出。在许多情况下,这将加速平均构建时间。在《Introducing Gradle Build Cache》博客文章中了解更多信息。

构建缓存对于切换分支也极其有用,因为前一次构建的输出得以保留,无需重新创建。性能节省与上述缓存构建相当,其中 Gradle 对于测试项目比 Maven 快 17 到 100 倍。

后续步骤

现在使用 Gradle 构建工具可以实现极端的性能提升。

为了让您高效地实现并保持这些数字,我们创建了Develocity 平台,它包含

  • 一个高度可扩展的远程构建缓存,带有方便的管理工具
  • Build Scan™,它允许您的组织深入了解开发人员在本地或 CI 服务器上运行的构建。利用这些数据来减少故障并持续提高构建的速度和可靠性。此外,它们将复杂的构建问题调试从非常困难变为非常高效。

性能只是构建系统众多重要特性中的一个,还有依赖管理和有效处理多个仓库等。请查看 Gradle 和 Maven 之间的功能比较矩阵

选择构建系统时,吸引力和采用率也是重要标准。毕竟,迁移构建是一个重要的长期决策。Gradle 最近被评为所有开源项目的前 20 名