配置缓存介绍

目录

简介

这是关于增量开发的系列博客文章的第二篇——增量开发是软件开发过程中进行频繁小改动的部分。我们将讨论即将推出的 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 构建 #

另一个值得注意的例子是一个非常大的真实世界 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 频道中获得帮助。

讨论