Gradle 与 Maven:性能比较
更快构建的商业案例
Gradle 旨在帮助组织更快地交付更好的软件。更快的构建是实现这一目标的最直接方法之一;当工程师不必等待软件构建时,他们有更多机会交付软件。CI 实例在不必要地重新构建时花费的时间更少,需要更少的 CPU 资源,并更早地提醒工程师。
案例研究
在 2017 年,我们与一支拥有 600 名工程师的团队合作。平均而言,每位工程师每周运行约 42 次构建。团队估计每位工程师每分钟的成本为 1.42 美元,并且他们每年至少工作 44 周。
这意味着他们每分钟可以使开发人员构建速度更快,他们就可以节省 160 万美元的工程师等待时间。
600 名工程师 * 1.42 美元/分钟 * 42 次构建/周 * 44 个工作周/年 = 160 万美元/年
如果构建速度提高 90% 会怎样?
性能改进具有额外的强大经济影响。例如,花费更少时间不必要地重建的 CI 实例也需要更少的 CPU 资源,更早地提醒工程师,并构建更小的变更集,这使得调试 CI 故障更容易。
你呢?
我们一次又一次地发现,投资构建性能的组织通常会很快收回投资,更快地发布产品,并拥有更快乐、更高效的团队。
更快的构建也有一些不太明显的优势。在关于 速度、性能和人类感知 的入门指南中,Ilya Grigorik 假设心理上下文切换可能在短短几秒钟后发生——使用极快构建工具的工程师更有可能保持“心流”状态,从而整体上更高效。
Gradle 比 Maven 快 100 倍
我们对各种项目在常见场景中进行了性能测量,以近似估算从 Maven 迁移到 Gradle 的成本摊销。
以下结果是在 MacBook Pro 2018、2.9 GHz 英特尔酷睿 i9、32 GB 内存、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
场景的并排比较,以便您可以直观地了解差异(不使用构建缓存)。
场景:中等规模的多项目构建
以下是针对单个存储库中包含 100 个模块的多项目构建的常见任务的结果。每个子项目包含 100 个源文件和 100 个测试文件。
对于 clean 构建,Gradle 的速度是 Maven 的 4-5 倍;对于增量更改,速度快约 40 倍;当 Gradle 任务输出被缓存时,速度快至多 13 倍。
场景:大型多项目构建
以下是针对单个存储库中包含 500 个模块的多项目构建的常见任务的结果。每个子项目包含 100 个源文件和 100 个测试文件。
对于 clean 构建,Gradle 的速度是 Maven 的 3-10 倍;对于增量更改,速度快约 85 倍;当 Gradle 任务输出被缓存时,速度快至多 13 倍。
场景:大型单体应用程序
虽然将所有代码放在一个项目中并不常见,但拥有多模块构建的情况非常普遍,其中大部分代码位于一个或几个模块中,这些模块比其他模块大得多。此场景是对此类项目的近似值——一个包含 50000 个源文件和 50000 个测试文件的单个项目。
对于 clean 构建,Gradle 的速度是 Maven 的 2-3 倍;对于增量更改,速度快约 7 倍;当 Gradle 任务输出被缓存时,速度快至多 3 倍。
性能结果摘要
- 在所有结果中,Gradle 的速度至少是 Maven 的 2 倍。
- 在构建 增量更改 时,Gradle 的速度是 Maven 的 7 到 85 倍;随着子项目数量的增加,优势也会增加。
- 当任务输出可以从 Gradle 的 构建缓存 中解析时,Gradle 构建的速度是 Maven 构建的 3 到 30 倍。
使这成为可能的 Maven 性能优势
选择 Gradle,您将受益于与 Apache Maven 相比的巨大性能提升:Gradle 实现了多种策略来使您的构建更快
- the 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 的增量编译已损坏(参见 此错误、另一个错误 或 此错误),您被迫始终执行干净构建。相反,Gradle 构建可靠且速度更快(与干净构建相比),前提是只有几个源文件发生了更改。
在 ABI 兼容更改场景中,我们对方法的实现应用更改,这是一种不会更改组件的应用程序二进制接口 (ABI) 的更改。
在 ABI 不兼容更改场景中,我们对公共方法的公共签名应用更改。您可以在我们的 关于编译避免和增量编译的博文 中了解更多信息。
关于 Gradle 的构建缓存
Gradle 构建缓存 在本地重用 Gradle 任务的输出,并在机器之间共享任务输出。在许多情况下,这将加速平均构建时间。在 介绍 Gradle 构建缓存 博文中了解更多信息。
构建缓存在分支之间切换时也非常有用,因为先前构建的构建输出将被保留,无需重新创建。性能节省与上面的缓存构建相当,其中 Gradle 比 Maven 快 17 到 100 倍,用于测试项目。
下一步
Gradle 构建工具现在可以实现极高的性能提升。
为了让您高效地实现并保持这些性能,我们创建了 Develocity 平台,它包含以下功能:
- 一个高度可扩展的 远程构建缓存,并提供便捷的管理工具
- Build Scan™,它允许您的组织深入了解开发人员本地或 CI 服务器上运行的构建。利用这些数据减少停机时间,并持续提高构建的速度和可靠性。此外,它们将调试复杂的构建问题从非常困难转变为非常高效。
性能只是构建系统的重要质量之一,其他质量还包括依赖项管理和与多个存储库的有效协作。查看 Gradle 和 Maven 的功能比较矩阵。
在选择构建系统时,牵引力和采用率也是重要的标准。毕竟,迁移构建是一个重大的决定,也是一个长期的决定。Gradle 最近 位列所有开源项目的前 20 名。