查看原文
其他

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

程序员江同学 程序员江同学 2022-09-09

前言

应用的启动优化是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


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存