使用 Macrobenchmark 库基准测试基线配置文件

我们建议使用 Jetpack Macrobenchmark 测试启用基线配置文件时应用的性能,然后将这些结果与禁用基线配置文件的基准测试进行比较。使用这种方法,您可以测量应用的启动时间(初始显示和完全显示的时间)或运行时渲染性能,以查看生成的帧是否会导致卡顿。

Macrobenchmarks 允许您使用 CompilationMode API 控制预测量编译。使用不同的 CompilationMode 值来比较不同编译状态下的性能。以下代码片段显示了如何使用 CompilationMode 参数来衡量基线配置文件带来的好处

@RunWith(AndroidJUnit4ClassRunner::class)
class ColdStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    // No ahead-of-time (AOT) compilation at all. Represents performance of a
    // fresh install on a user's device if you don't enable Baseline Profiles—
    // generally the worst case performance.
    @Test
    fun startupNoCompilation() = startup(CompilationMode.None())

    // Partial pre-compilation with Baseline Profiles. Represents performance of
    // a fresh install on a user's device.
    @Test
    fun startupPartialWithBaselineProfiles() =
        startup(CompilationMode.Partial(baselineProfileMode = BaselineProfileMode.Require))

    // Partial pre-compilation with some just-in-time (JIT) compilation.
    // Represents performance after some app usage.
    @Test
    fun startupPartialCompilation() = startup(
        CompilationMode.Partial(
            baselineProfileMode = BaselineProfileMode.Disable,
            warmupIteration = 3
        )
    )

    // Full pre-compilation. Generally not representative of real user
    // experience, but can yield more stable performance metrics by removing
    // noise from JIT compilation within benchmark runs.
    @Test
    fun startupFullCompilation() = startup(CompilationMode.Full())

    private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
        packageName = "com.example.macrobenchmark.target",
        metrics = listOf(StartupTimingMetric()),
        compilationMode = compilationMode,
        iterations = 10,
        startupMode = StartupMode.COLD,
        setupBlock = {
            pressHome()
        }
    ) {
        // Waits for the first rendered frame, which represents time to initial display.
        startActivityAndWait()

        // Waits for content to be visible, which represents time to fully drawn.
        device.wait(Until.hasObject(By.res("my-content")), 5_000)
    }
}

在以下屏幕截图中,您可以直接在 Android Studio 中查看 Now in Android 示例 应用在 Google Pixel 7 上运行的结果。结果显示,使用基线配置文件 (229.0 毫秒) 时应用启动速度最快,而没有编译时 (324.8 毫秒)。

results of ColdstartupBenchmark
图 1. ColdStartupBenchmark 的结果显示了无编译 (324 毫秒)、完全编译 (315 毫秒)、部分编译 (312 毫秒) 和基线配置文件 (229 毫秒) 的初始显示时间。

虽然前面的示例显示了使用 StartupTimingMetric 捕获的应用启动结果,但还有其他一些值得考虑的重要指标,例如 FrameTimingMetric。有关所有指标类型的更多信息,请参阅 捕获 Macrobenchmark 指标

完全显示时间

前面的示例测量了首次显示时间 (TTID),即应用生成其第一帧所花费的时间。但是,这并不一定反映用户可以开始与您的应用交互的时间。 完全显示时间 (TTFD) 指标在测量和优化使应用状态完全可用所需的代码路径方面更有用。

我们建议优化 TTID 和 TTFD,因为两者都很重要。较低的 TTID 有助于用户了解应用实际上已启动。保持较短的 TTFD 对于确保用户能够快速与应用交互非常重要。

有关在应用 UI 完全绘制时进行报告的策略,请参阅提高启动计时精度

  • 注意:当 JavaScript 关闭时,将显示链接文本
  • [编写 Macrobenchmark][11]
  • [捕获 Macrobenchmark 指标][12]
  • [应用启动分析和优化 {:#app-startup-analysis-optimization}][13]