介绍配置缓存

这是关于增量开发的一系列博客文章的第二篇,增量开发是软件开发过程中的一个部分,您会在其中进行频繁的小改动。我们将讨论即将推出的 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 构建。它有 100 个子项目和相当复杂的构建逻辑。你可以用它来重现这些结果。在进行实现更改后运行测试,从 16.4 秒降至 13.8 秒,跳过了大约 2 秒的配置阶段。

蓝色表示配置阶段,绿色表示执行阶段。在左侧,没有启用配置缓存的情况下,配置阶段花费超过 2 秒,而在右侧启用配置缓存后,配置阶段降至 214 毫秒。

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

Android 构建

另一个值得注意的例子是一个非常大的真实世界 Android 构建,拥有约 2500 个子项目。在该构建中,运行 :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 插件预览版 4.2.0-alpha07 支持配置缓存。截至撰写本文时,最新的 Kotlin Gradle 插件 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 频道中获得帮助。

讨论