编写宏基准测试

使用宏基准测试库测试您的应用中较大的用例,包括应用启动和复杂的界面操作,例如滚动 RecyclerView 或运行动画。如果您想测试代码的较小区域,请参阅微基准测试库。本页面介绍了如何设置宏基准测试库。

该库会将基准测试结果输出到 Android Studio 控制台以及一个包含更多详细信息的 JSON 文件中。它还提供了您可以在 Android Studio 中加载和分析的跟踪文件。

在持续集成 (CI) 环境中使用宏基准测试库,如持续集成中的基准测试中所述。

您可以使用宏基准测试生成基线配置文件。首先,设置宏基准测试库,然后您可以创建基线配置文件

项目设置

我们建议您使用最新版本的 Android Studio 和宏基准测试,以利用 IDE 中与宏基准测试集成的功能。

设置宏基准测试模块

宏基准测试需要一个 com.android.test 模块(与您的应用代码分离),该模块负责运行测量您应用的测试。

在 Android Studio 中,有一个模板可用于简化宏基准测试模块的设置。基准测试模块模板会自动在您的项目中创建一个模块,用于测量由应用模块构建的应用,其中包括一个示例启动基准测试。

要使用模块模板创建新模块,请执行以下操作

  1. 在 Android Studio 的 Project 面板中右键点击您的项目或模块,然后选择 New > Module

  2. Templates 窗格中选择 Benchmark。您可以自定义目标应用(即要进行基准测试的应用),以及新宏基准测试模块的软件包名称和模块名称。

  3. 点击 Finish

Benchmark Module
template

图 1. 基准测试模块模板。

设置应用

要对应用(即宏基准测试的目标)进行基准测试,该应用必须是 profileable 的,这样可以在不影响性能的情况下读取详细的跟踪信息。模块向导会自动将 <profileable> 标记添加到应用的 AndroidManifest.xml 文件中。

请确保目标应用包含 ProfilerInstaller 1.3 或更高版本,这是宏基准测试库启用配置文件捕获和重置以及着色器缓存清除所必需的。

将基准测试应用配置得尽可能接近发布版本或生产版本。将其设置为不可调试,最好启用混淆,这可以提高性能。您通常通过创建发布变体的副本(其性能相同,但使用调试密钥在本地签名)来执行此操作。或者,您可以使用 initWith 来指示 Gradle 为您执行此操作

Kotlin

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
    }

    create("benchmark") {
        initWith(getByName("release"))
        signingConfig = signingConfigs.getByName("debug")
    }
}

Groovy

buildTypes {
    getByName("release") {
        isMinifyEnabled = true
        isShrinkResources = true
        proguardFiles(
            getDefaultProguardFile("proguard-android-optimize.txt"),
            "proguard-rules.pro"
        )
        // In real app, this would use its own release keystore
        signingConfig = signingConfigs.getByName("debug")
    }
}

为了确保基准测试能够构建和测试您应用的正确变体,如图 2 所示,请执行以下操作

  1. 执行 Gradle 同步。
  2. 打开 Build Variants 面板。
  3. 选择应用和宏基准测试模块的基准测试变体。

Select benchmark
variant

图 2. 选择基准测试变体。

(可选)设置多模块应用

如果您的应用有多个 Gradle 模块,请确保您的构建脚本知道要编译哪个构建变体。将matchingFallbacks 属性添加到 :macrobenchmark:app 模块的 benchmark 构建类型中。其余的 Gradle 模块可以与以前具有相同的配置。

Kotlin

create("benchmark") {
    initWith(getByName("release"))
    signingConfig = signingConfigs.getByName("debug")

    matchingFallbacks += listOf("release")
 }

Groovy

benchmark {
    initWith buildTypes.release
    signingConfig signingConfigs.debug

    matchingFallbacks = ['release']
 }

否则,新添加的 benchmark 构建类型会导致构建失败并提供以下错误消息

> Could not resolve project :shared.
     Required by:
         project :app
      > No matching variant of project :shared was found.
      ...

在您的项目中选择构建变体时,为 :app:macrobenchmark 模块选择 benchmark,为应用中所有其他模块选择 release,如图 3 所示

Benchmark variants for multi-module project with release and benchmark build
types
selected

图 3. 具有 release 和 benchmark 构建类型的多模块项目的基准测试变体已选中。

如需了解更多信息,请参阅使用具备变体识别功能的依赖项管理

(可选)设置产品变种

如果您的应用中设置了多个产品变种,请配置 :macrobenchmark 模块,以便它知道要构建和基准测试您应用的哪个产品变种。

本页面中的示例使用了 :app 模块中的两个产品变种:demoproduction,如以下代码段所示

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
        // ...
    }
    create("production") {
        dimension = "environment"
        // ...
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
        // ...
    }

    production {
        dimension 'environment'
        // ...
    }
}

如果没有此配置,您可能会遇到与多个 Gradle 模块类似​​的构建错误

Could not determine the dependencies of task ':macrobenchmark:connectedBenchmarkAndroidTest'.
> Could not determine the dependencies of null.
   > Could not resolve all task dependencies for configuration ':macrobenchmark:benchmarkTestedApks'.
      > Could not resolve project :app.
        Required by:
            project :macrobenchmark
         > The consumer was configured to find a runtime of a component, as well as attribute 'com.android.build.api.attributes.BuildTypeAttr' with value 'benchmark', attribute 'com.android.build.api.attributes.AgpVersionAttr' with value '7.3.0'. However we cannot choose between the following variants of project :app:
             -   demoBenchmarkRuntimeElements
             -   productionBenchmarkRuntimeElements
           All of them match the consumer attributes:
           ...

以下两个部分介绍了如何使用多个产品变种配置基准测试。

使用 missingDimensionStrategy

:macrobenchmark 模块的 defaultConfig 中指定 missingDimensionStrategy 会告诉构建系统回退到变种维度。如果您在模块中找不到它们,请指定要使用的维度。在以下示例中,production 变种用作默认维度

Kotlin

defaultConfig {
    missingDimensionStrategy("environment", "production")
}

Groovy

defaultConfig {
    missingDimensionStrategy "environment", "production"
}

这样,:macrobenchmark 模块就能够只构建和基准测试指定的产品变种,这在您知道只有一个产品变种具有适当配置可进行基准测试时非常有用。

在 :macrobenchmark 模块中定义产品变种

如果您想构建和基准测试其他产品变种,请在 :macrobenchmark 模块中定义它们。像在 :app 模块中一样指定它,但只将 productFlavors 分配给一个 dimension。不需要其他设置

Kotlin

flavorDimensions += "environment"
productFlavors {
    create("demo") {
        dimension = "environment"
    }
    create("production") {
        dimension = "environment"
    }
}

Groovy

flavorDimensions 'environment'
productFlavors {
    demo {
        dimension 'environment'
    }

    production {
        dimension 'environment'
    }
}

定义并同步项目后,从 Build Variants 窗格中选择相关的构建变体,如图 4 所示

Benchmark variants for project with product flavors showing
productionBenchmark and release
selected

图 4. 具有产品变种的项目的基准测试变体,显示“productionBenchmark”和“release”已选中。

如需了解更多信息,请参阅解决与变体匹配相关的构建错误

创建宏基准测试类

基准测试通过宏基准测试库中的 MacrobenchmarkRule JUnit4 规则 API 提供。它包含一个 measureRepeated 方法,可让您指定运行和基准测试目标应用的各种条件。

您至少需要指定目标应用的 packageName、您想要测量的 metrics 以及基准测试必须运行的 iterations 次数。

Kotlin

@LargeTest
@RunWith(AndroidJUnit4::class)
class SampleStartupBenchmark {
    @get:Rule
    val benchmarkRule = MacrobenchmarkRule()

    @Test
    fun startup() = benchmarkRule.measureRepeated(
        packageName = TARGET_PACKAGE,
        metrics = listOf(StartupTimingMetric()),
        iterations = DEFAULT_ITERATIONS,
        setupBlock = {
            // Press home button before each run to ensure the starting activity isn't visible.
            pressHome()
        }
    ) {
        // starts default launch activity
        startActivityAndWait()
    }
}

Java

@LargeTest
@RunWith(AndroidJUnit4.class)
public class SampleStartupBenchmark {
    @Rule
    public MacrobenchmarkRule benchmarkRule = new MacrobenchmarkRule();

    @Test
    public void startup() {
        benchmarkRule.measureRepeated(
            /* packageName */ TARGET_PACKAGE,
            /* metrics */ Arrays.asList(new StartupTimingMetric()),
            /* iterations */ 5,
            /* measureBlock */ scope -> {
                // starts default launch activity
                scope.startActivityAndWait();
                return Unit.INSTANCE;
            }
        );
    }
}

有关自定义基准测试的所有选项,请参阅自定义基准测试部分。

注意: 在进行基准测试时,请使用 Android 14(API 级别 34)或更高版本来保留状态。宏基准测试库会为每次编译完全重置编译状态,这在 Android 14 之前的版本中需要重新安装 APK。作为一种变通方法,请单独控制应用编译,并使用 CompilationMode.Ignore 跳过编译。

在 Android Studio 中运行测试以测量应用在设备上的性能。您可以使用测试类或方法旁边的 gutter 操作(如图 5 所示)运行基准测试,就像运行任何其他 @Test 一样。

Run macrobenchmark with gutter action next to test
class

图 5. 使用测试类旁边的 gutter 操作运行宏基准测试。

您还可以通过执行 connectedCheck 命令从命令行运行 Gradle 模块中的所有基准测试

./gradlew :macrobenchmark:connectedCheck

您可以通过执行以下命令运行单个测试

./gradlew :macrobenchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.macrobenchmark.startup.SampleStartupBenchmark#startup

有关如何在持续集成中运行和监控基准测试的信息,请参阅持续集成中的基准测试

基准测试结果

成功运行基准测试后,指标会直接显示在 Android Studio 中,并以JSON 文件的形式输出供 CI 使用。每个测量迭代都会捕获一个单独的系统跟踪。您可以通过点击 Test Results 窗格中的链接来打开这些跟踪结果,如图 6 所示

Macrobenchmark startup
results

图 6. 宏基准测试启动结果。

加载跟踪后,Android Studio 会提示您选择要分析的进程。选择项会预填充目标应用进程,如图 7 所示

Studio trace process
selection

图 7. Studio 跟踪进程选择。

加载跟踪文件后,Studio 会在CPU 分析器工具中显示结果

Studio
Trace

图 8. Studio 跟踪。

JSON 报告和任何性能分析跟踪也会自动从设备复制到主机。这些文件写入主机上的以下位置

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/

手动访问跟踪文件

如果您想使用 Perfetto 工具分析跟踪文件,则需要额外的步骤。Perfetto 允许您检查跟踪期间设备上发生的所有进程,而 Android Studio 的 CPU 分析器将检查限制为单个进程。

如果您从 Android Studio 或 Gradle 命令行调用测试,跟踪文件会自动从设备复制到主机。这些文件写入主机上的以下位置

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/TrivialStartupBenchmark_startup[mode=COLD]_iter002.perfetto-trace

当您的主机系统中有跟踪文件时,您可以通过菜单中的 File > Open 在 Android Studio 中打开它。这会显示上一节中所示的分析器工具视图。

配置错误

如果应用配置错误(可调试或不可分析),宏基准测试会返回错误,而不是报告不正确或不完整的测量结果。您可以使用 androidx.benchmark.suppressErrors 参数来抑制这些错误。

宏基准测试在尝试测量模拟器或低电量设备时也会返回错误,这可能会影响核心可用性和时钟速度。

自定义基准测试

measureRepeated 函数接受各种参数,这些参数会影响库收集的指标、应用的启动和编译方式,或基准测试运行的迭代次数。

捕获指标

指标是从您的基准测试中提取的主要信息类型。以下指标可用

有关指标的更多信息,请参阅捕获宏基准测试指标

使用自定义事件改进跟踪数据

使用自定义跟踪事件来插桩您的应用会很有用,这些事件会与跟踪报告的其余部分一起显示,并有助于指出特定于您应用的问题。要了解有关创建自定义跟踪事件的更多信息,请参阅定义自定义事件

CompilationMode

宏基准测试可以指定 CompilationMode,它定义了应用有多少部分必须从 DEX 字节码(APK 中的字节码格式)预编译为机器码(类似于预编译的 C++)。

默认情况下,宏基准测试使用 CompilationMode.DEFAULT 运行,这会在 Android 7(API 级别 24)及更高版本上安装基线配置文件(如果可用)。如果您使用的是 Android 6(API 级别 23)或更早版本,编译模式会按照默认系统行为完全编译 APK。

如果目标应用同时包含基线配置文件和 ProfileInstaller 库,则可以安装基线配置文件。

在 Android 7 及更高版本上,您可以自定义 CompilationMode 以影响设备上的预编译量,从而模拟不同级别的预先 (AOT) 编译或 JIT 缓存。请参阅 CompilationMode.FullCompilationMode.PartialCompilationMode.NoneCompilationMode.Ignore

此功能基于 ART 编译命令构建。每个基准测试在开始前都会清除配置文件数据,以帮助确保基准测试之间互不干扰。

StartupMode

要执行活动启动,您可以传递预定义的启动模式:COLDWARMHOT。此参数会改变活动启动方式以及测试开始时的进程状态。

要了解有关启动类型的更多信息,请参阅应用启动时间

示例

GitHub 存储库中的宏基准测试示例中提供了一个示例项目。

提供反馈

要报告 Jetpack 宏基准测试的问题或提交功能请求,请参阅公共问题跟踪器