Google I/O : 使用 Baseline Profiles 优化启动性能

4,039 阅读5分钟

前言

应用的启动优化是Android性能优化中的一个常见问题,除了常规的一些优化手段,在本次Google/IO大会还介绍了通过Baseline Profiles来优化启动速度。
本文主要包括以下内容:

  1. Baseline Profiles是什么?
  2. 如何使用Baseline Profiles?
  3. 使用Baseline Profiles的效果怎么样?

Baseline Profiles是什么?

Android 9Play Cloud 中引入了 ART 优化Profiles文件,以缩短应用启动时间。 平均而言,我们发现,当云Profiles文件可用时,应用程序的冷启动速度至少快 15%。

Profiles文件如何工作?

当应用程序在安装或更新后首次启动时,其代码在解释模式下运行(也就是JIT)。 在 APK 中,JavaKotlin 代码被编译为 dex 字节码,但由于存储和加载完全编译的App的成本,并未完全编译为机器码。 应用程序中经常使用的类和方法,以及用于应用程序启动的类和方法,都记录在Profiles中。 一旦设备进入空闲模式,ART 就会根据这些Profiles编译应用程序。 这加快了后续应用程序的启动。

Android 9开始,Google Play 还提供 Cloud Profiles。 当应用在设备上运行时,由 ART 生成的配置文件由 Play 商店应用上传并在云端聚合。 一旦为应用程序上传了足够的配置文件,Play 应用程序就会使用汇总的配置文件进行后续安装。

存在的问题

虽然Cloud Profiles在可用时很棒,但在安装应用程序时并不总是可以使用。 收集和汇总配置文件通常需要几天时间,如果你的App是每周更新时,就可能在Cloud Profile可用之前就发生更新了。 因此,Google Android 团队开始寻找其他方法来改善Profiles的延迟。

解决方案

Baseline Profiles是一种新的机制,可提供可在 Android 7及更高版本上使用的配置文件。 Baseline Profiles是由 Android Gradle 插件使用生成的 ART 配置文件,示例如下所示:

HSPLandroidx/compose/runtime/ComposerImpl;->updateValue(Ljava/lang/Object;)V
HSPLandroidx/compose/runtime/ComposerImpl;->updatedNodeCount(I)I
HLandroidx/compose/runtime/ComposerImpl;->validateNodeExpected()V
PLandroidx/compose/runtime/CompositionImpl;->applyChanges()V
HLandroidx/compose/runtime/ComposerKt;->findLocation(Ljava/util/List;I)I

Baseline Profiles是在构建期间创建的,作为 APK 的一部分提供给 Play Store,然后在下载应用程序时从 Play Store 发送给用户。 当云配置文件尚不可用时,它们填补了 ART 云配置文件管道中的空白,并在它们可用时自动与云配置文件合并。

Baseline Profiles的最大好处之一是它们可以在本地开发和测试,因此开发人员可以看到用户的实际体验。 同时从Android 7就已经支持了,而Cloud Profiles只支持Android 9以后的版本

如何使用Baseline Profiles?

所有应用程序和库开发人员都可以从Baseline Profiles中受益。 理想情况下,开发人员为其最关键的用户路径创建配置文件,以确保这些关键路径具有始终如一的快速性能,无论云配置文件是否可用。
关于如何创建Baseline Profiles的详细步骤,可查看:创建基准配置文件

在通过以上方式生成了配置文件之后,将生成的文件重命名为baseline-prof.txt,并将其复制到应用模块的 src/main 目录,就可以生效了

如果您现在还没有准备好为您的应用程序生成Baseline Profiles,您也可以通过更新依赖项来从中受益。 如果您使用AGP 7.1.0-alpha05 或更高版本进行构建,您将获得包含在您的 APK 中的Baseline Profiles,这些配置文件已经由库(例如 Jetpack)默认提供。 Google Play 在安装时使用这些配置文件编译您的应用程序。

使用Baseline Profiles的效果怎么样?

第三方库的代码在默认情况下是没有完全编译的,如果它在启动的关键路径上做了大量工作,可能会带来一些性能问题。
比如Jetpack Compose 是一个 UI 库,它不是 Android 系统的一部分,因此在安装时并未完全编译,这与许多 Android View 工具包代码不同。这也是Compose在可能刚启动时比较慢的原因.
为了解决这个问题,Compose 默认提供了Baseline Profiles,可减少 Compose 应用程序的启动时间和卡顿。

GogleGoogle MapGoogle Play中都使用了Baseline Profiles,根据Google I/O上给出的数据,可以达到30%左右的启动性能提升

测量性能提升

由于Jetpack包和Compose都提供了默认的Baseline Profiles配置,因此你自定义的Baseline Profiles不一定会达到最佳效果,在自定义时必须也要编写相关性能测试用例

abstract class AbstractStartupBenchmark(private val startupMode: StartupMode) {
    @Test
    fun startupNoCompilation() = startup(CompilationMode.None())

    @Test
    fun startupBaselineProfileDisabled() = startup(
        CompilationMode.Partial(baselineProfileMode = Disable, warmupIterations = 1)
    )

    @Test
    fun startupBaselineProfile() = startup(CompilationMode.Partial(baselineProfileMode = Require))

    @Test
    fun startupFullCompilation() = startup(CompilationMode.Full())
}

如上所示,分别测试CompilationMode.None,CompilationMode.Full,CompilationMode.Partial等情况下的启动性能,根据结果分析是否需要自定义Baseline Profiles

总结

通过Baseline Profiles,将应用启动时的关键路径相关代码提前进行编译,可以加快启动速度与首次加载速度。您可以更新到AGP 7.1.0-alpha05 或更高,来享受到Jetpack库提供的默认配置,也可以自定义Baseline Profiles,但是在自定义后需要通过Macrobenchmark库测量性能,以衡量自定义配置是否有效

参考资料

Google I/0 应用性能的新动态
Improving App Performance with Baseline Profiles