引入配置缓存

目录

引言

这是关于增量开发系列博文的第二篇,增量开发是软件开发过程中频繁进行小幅更改的部分。我们将讨论即将推出的 Gradle 构建工具功能,这些功能将显著缩短这种用例的反馈时间。在上一篇文章中,我们介绍了 Gradle 6.5 的文件系统监视

Gradle 6.6中,我们引入了一个名为配置缓存的实验性功能,它通过缓存配置阶段的结果并在后续构建中重用,从而显著提高了构建性能。使用配置缓存,当没有任何影响构建配置(例如构建脚本)的变化时,Gradle 可以完全跳过配置阶段。

在此之上,重用配置缓存时,默认情况下会并行运行更多工作,并且依赖项解析也会被缓存。配置和执行阶段的隔离以及任务的隔离,使得这些优化成为可能。

请注意,配置缓存与构建缓存不同,后者缓存构建生成的输出。配置缓存只捕获配置阶段的状态。它也与 IDE 同步和导入过程分开,这些过程目前不受益于配置缓存。

为了缓存配置结果,Gradle 施加了一些严格的要求,插件和构建脚本需要遵循这些要求。许多插件,包括一些核心 Gradle 插件,尚未满足这些要求。此外,某些 Gradle 功能对配置缓存的支持尚未实现。因此,您的构建和您依赖的插件可能需要更改才能满足这些要求。Gradle 将报告在您的构建逻辑中发现的问题,以帮助您使构建与配置缓存一起工作。

配置缓存目前高度实验性,默认未启用。我们提前发布它,以便在稳定新功能的同时收集社区反馈。

尽管如此,我们致力于使配置缓存达到生产就绪状态,并最终目标是默认启用它。您可以期待它在接下来的 Gradle 版本中会显著改进。

配置缓存实战 #

建议从最简单的任务调用开始。启用配置缓存运行help是一个很好的第一步

Running help

这是它在 Android Studio 中运行的实际情况,将中等大小的Santa Tracker Android应用程序部署到 Android 模拟器,更改雪花在屏幕上移动的方式并将更改应用到模拟器

构建时间优化 #

此功能的实际影响取决于多种因素,但总的来说,它应该会显著减少构建时间。我们已经在大型真实构建中看到了显著的改进,让我们来看看其中一些。

Java 构建 #

在一个拥有约 500 个子项目和复杂构建逻辑的大型 Java 企业构建中,运行 :help 从 8 秒降至 0.5 秒。这快了 16 倍。当然,运行 :help 并没有那么有用,但它能让我们了解配置阶段节省的时间。在同一个构建中,更改一些实现代码后运行 assemble 从约 40 秒降至约 13 秒,这快了约 3 倍。

现在我们来看gradle/gradle构建。它有数百个子项目和相当复杂的构建逻辑。您可以使用它来重现这些结果。在进行实现更改后运行测试,时间从 16.4 秒下降到 13.8 秒,跳过了大约 2 秒的配置阶段。

蓝色部分是配置阶段,绿色部分是执行阶段。左侧,没有启用配置缓存时,配置阶段需要超过 2 秒;右侧,启用配置缓存后,配置阶段降至 214 毫秒。

你也可以看到执行阶段受益于配置缓存,但在这种情况下,它主要由编译和运行测试决定。

Android 构建 #

另一个值得注意的例子是一个拥有约 2500 个子项目的超大型真实 Android 构建。在该构建中,运行 :help 从约 25 秒降至约 0.5 秒,这快了 50 倍!运行一个更有用的构建,例如在更改某些实现后组装 APK,从约 50 秒降至约 20 秒,几乎快了 3 倍。

Santa Tracker Android 项目中,我们发现小幅实现更改的构建时间有以下改进:

配置阶段缩短了一半,从 129 毫秒降至 63.5 毫秒。您还可以看到,由于更多的任务并行化和依赖解析的缓存,执行阶段因配置缓存而加速。

如果您想使用上述构建进行复现或测量您自己的构建,您可以按照此存储库中的说明使用Gradle Profiler。请注意,Gradle Profiler 会显示略有不同的情况,更接近 IDE 的体验,因为两者都使用 Gradle Tooling API。这会跳过在使用命令行时启动 Gradle 客户端 JVM 的固定开销。

它是如何工作的? #

当配置缓存启用并且您运行 Gradle 执行一组特定的任务时,例如通过运行 ./gradlew check,Gradle 会检查所请求的任务集是否有可用的配置缓存条目。如果可用,Gradle 会使用此条目而不是运行配置阶段。缓存条目包含有关要运行的任务集及其配置和依赖信息。

第一次运行一组特定任务时,配置缓存中不会有这些任务的条目,因此 Gradle 将正常运行配置阶段

  1. 运行初始化脚本。
  2. 运行构建的设置脚本,应用所有请求的设置插件。
  3. 如果存在,配置并构建 buildSrc 项目。
  4. 运行构建脚本,应用所有请求的项目插件。
  5. 计算请求任务的任务图,运行所有延迟配置操作。

配置阶段之后,Gradle 将任务图的状态写入配置缓存,并拍摄快照以供后续 Gradle 调用。然后,执行阶段正常运行。这意味着您首次运行一组特定任务时,不会看到任何构建性能改进。

当您随后再次使用同一组任务运行 Gradle 时,例如再次运行 ./gradlew check,Gradle 将直接从配置缓存加载任务及其配置,完全跳过配置阶段,并并行运行所有任务。在使用配置缓存条目之前,Gradle 会检查该条目的“构建配置输入”(例如构建脚本)是否发生变化。如果构建配置输入已更改,Gradle 将不会使用该条目,并将如上所述再次运行配置阶段,并保存结果以供后续重用。

要求和限制 #

为了将任务图的状态捕获到配置缓存并在后续构建中重新加载它,Gradle 对任务和其他构建逻辑施加了某些要求。这些要求中的每一项都被视为配置缓存“问题”,并且默认情况下,如果存在违规,则会导致构建失败。

如果在缓存或重用配置时发现任何问题,将生成一份 HTML 报告以帮助您诊断和修复问题。

Problems Report

如果您遇到此类问题,您的构建或正在使用的 Gradle 插件可能需要调整。有关如何使用此报告的更多信息,请参阅文档的故障排除部分。

您可以在配置缓存文档中找到受支持的核心插件集和尚未实现的 Gradle 功能集。

在撰写本文时,最新的 Android Gradle Plugin 预览版 4.2.0-alpha07 支持配置缓存。最新的 Kotlin Gradle Plugin 在撰写本文时为 1.4.0-RC,在简单的 JVM 项目上运行并报告了一些问题。Kotlin 1.4.20 是完全兼容插件的当前目标版本。此信息可在 gradle/gradle#13490 中找到,其中还包括最常用社区插件的状态。

尝试配置缓存 #

如果您想了解您的项目如何从配置缓存中受益,以下是您可以尝试的方法。

首先,确保您运行的是 Gradle 6.6 或更高版本。为了启用配置缓存,您需要在命令行中传递 --configuration-cache。或者,将以下内容添加

org.gradle.unsafe.configuration-cache=true

到项目目录或 Gradle 用户主目录中的 gradle.properties 文件中,这样您就不需要在每次构建时都传递命令行选项。就是这样:下一次构建将启用配置缓存运行。

请记住,只有在后续构建中,当请求的任务相同时且启用了该功能时,您才会看到性能提升。如果您想对您的构建进行基准测试,您可以轻松地使用Gradle Profiler,按照此存储库中的说明进行操作。

如果遇到任何问题,请查看支持的核心插件社区插件,学习如何在用户手册中故障排除。您还可以阅读我们推荐的采用步骤

如果您仍然有问题,如果您认为问题出在 Gradle 本身,请提交 Gradle 问题,或者查看gradle/gradle#13490中支持的社区插件问题。您也可以在 Gradle 社区 Slack#configuration-cache 频道中获得帮助。

讨论