编写微基准

您可以通过向应用代码中添加更改,快速开始使用 Microbenchmark 库。为了正确设置项目,请按照完整的项目设置一节操作,您需要对代码库进行更复杂的更改。

快速入门

本节提供的快速指南可帮助您尝试执行基准化分析以及执行一次性衡量,而无需将代码移到模块中。由于这些步骤涉及在应用中停用调试功能以获得准确的性能结果,因此您应该将其保留在本地工作副本中,而无需将更改提交至源代码控制系统中。

如需快速执行一次性基准化分析,请执行以下操作:

  1. 将基准库添加到模块的 build.gradle 文件中:

    project_root/module_dir/build.gradle

    Groovy

    dependencies {
        androidTestImplementation 'androidx.benchmark:benchmark-junit4:1.1.0-beta03'
    }
    

    Kotlin

    dependencies {
        androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.0-beta03")
    }
    
  2. 如需在测试清单中停用调试功能,请更新 <application> 元素以暂时强制停用调试功能:

    project_root/module_dir/src/androidTest/AndroidManifest.xml

    <!-- Important: disable debuggable for accurate performance results -->
    <application
        android:debuggable="false"
        tools:ignore="HardcodedDebugMode"
        tools:replace="android:debuggable"/>
    
  3. 如需添加基准,请在 androidTest 目录下的测试文件中添加 BenchmarkRule 的实例。如需详细了解如何编写基准,请参阅创建微基准类

    以下代码段展示了如何将基准添加到 JUnit 测试中:

    Kotlin

    @RunWith(AndroidJUnit4::class)
    class SampleBenchmark {
        @get:Rule
        val benchmarkRule = BenchmarkRule()
    
        @Test
        fun benchmarkSomeWork() {
            benchmarkRule.measureRepeated {
                doSomeWork()
            }
        }
    }
    

    Java

    @RunWith(AndroidJUnit4.class)
    class SampleBenchmark {
        @Rule
        public BenchmarkRule benchmarkRule = new BenchmarkRule();
    
        @Test
        public void benchmarkSomeWork() {
            final BenchmarkState state = benchmarkRule.getState();
            while (state.keepRunning()) {
                doSomeWork();
            }
        }
    }
    

如需了解如何编写基准,请跳转到创建微基准类部分。

完整的项目设置

如需设置常规基准化分析而非一次性基准化分析,请将基准分隔到专门的模块中。这样可确保其配置(例如将 debuggable 设置为 false)与常规测试分开。

由于微基准会直接运行您的代码,因此您需要将要进行基准化分析的代码放在单独的 Gradle 模块中,并设置对该模块的依赖项,如下图所示。

应用结构,其中包含 :app、:microbenchmark 和 :benchmarkable Gradle 模块,这些模块允许微基准对 :benchmarkable 模块中的代码进行基准化分析。

若要添加新的 Gradle 模块,您可以使用 Android Studio 中的模块向导。该向导会创建一个针对基准化分析进行了预先配置的模块,其中添加了基准目录并已将 debuggable 设置为 false

Bumblebee(或更高版本)

  1. 在 Android Studio 的 Project 面板中右键点击您的项目或模块,然后依次点击 New > Module
  2. Templates 窗格中选择 Benchmark
  3. 选择“Microbenchmark”作为基准模块类型。
  4. 输入“microbenchmark”作为模块名称。
  5. 点击 Finish
  6. 配置新的库模块

Arctic Fox

  1. 在 Android Studio 的 Project 面板中右键点击您的项目或模块,然后依次点击 New > Module
  2. Templates 窗格中选择 Benchmark
  3. 输入“microbenchmark”作为模块名称。
  4. 点击 Finish
  5. 配置新的库模块

创建模块后,更改其 build.gradle 文件,并将 testImplementation 添加到包含要进行基准测试的代码的模块:

Groovy

dependencies {
    // Note, that the module name may be different
    androidTestImplementation project(':benchmarkable')
}

Kotlin

dependencies {
    // Note, that the module name may be different
    androidTestImplementation(project(":benchmarkable"))
}

创建微基准类

基准是标准的插桩测试。如需创建基准,请使用基准库提供的 BenchmarkRule 类。如需对 Activity 进行基准化分析,请使用 ActivityTestRuleActivityScenarioRule。如需对界面代码进行基准化分析,请使用 @UiThreadTest

以下代码展示了一个示例基准:

Kotlin

@RunWith(AndroidJUnit4::class)
class SampleBenchmark {
    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            doSomeWork()
        }
    }
}
    

Java

@RunWith(AndroidJUnit4.class)
class SampleBenchmark {
    @Rule
    public BenchmarkRule benchmarkRule = new BenchmarkRule();

    @Test
    public void benchmarkSomeWork() {
        final BenchmarkState state = benchmarkRule.getState();
        while (state.keepRunning()) {
            doSomeWork();
        }
    }
}
    

为设置停用计时功能

您可以使用 runWithTimingDisabled{} 代码块对不想测量时间的代码段停用计时功能。这些代码段通常表示您需要在基准的每次迭代中运行的一些代码。

Kotlin

// using random with the same seed, so that it generates the same data every run
private val random = Random(0)

// create the array once and just copy it in benchmarks
private val unsorted = IntArray(10_000) { random.nextInt() }

@Test
fun benchmark_quickSort() {
    // ...
    benchmarkRule.measureRepeated {
        // copy the array with timing disabled to measure only the algorithm itself
        listToSort = runWithTimingDisabled { unsorted.copyOf() }

        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort)
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(listToSort.isSorted)
}
    

Java

private final int[] unsorted = new int[10000];

public SampleBenchmark() {
    // using random with the same seed, so that it generates the same data every run
    Random random = new Random(0);

    // create the array once and just copy it in benchmarks
    Arrays.setAll(unsorted, (index) -> random.nextInt());
}

@Test
public void benchmark_quickSort() {
    final BenchmarkState state = benchmarkRule.getState();

    int[] listToSort = new int[0];

    while (state.keepRunning()) {
        
        // copy the array with timing disabled to measure only the algorithm itself
        state.pauseTiming();
        listToSort = Arrays.copyOf(unsorted, 10000);
        state.resumeTiming();
        
        // sort the array in place and measure how long it takes
        SortingAlgorithms.quickSort(listToSort);
    }

    // assert only once not to add overhead to the benchmarks
    assertTrue(SortingAlgorithmsKt.isSorted(listToSort));
}
    

尽量减少在 measureRepeated 代码块以及 runWithTimingDisabled 内部完成的工作量。measureRepeated 代码块会运行多次,这会影响运行基准所需的总时间。如果您需要验证基准的某些结果,可以断言上一个结果,而不是在基准的每次迭代中执行此操作。

运行基准

在 Android Studio 中,像使用测试类或方法旁边的边线操作运行任何 @Test 一样运行基准,如下图所示。

使用测试类旁边的边线操作运行微基准测试

或者,您也可以在命令行中运行 connectedCheck,以运行来自指定 Gradle 模块的所有测试:

./gradlew benchmark:connectedCheck

或单个测试:

./gradlew benchmark:connectedCheck -P android.testInstrumentationRunnerArguments.class=com.example.benchmark.SampleBenchmark#benchmarkSomeWork

基准结果

微基准运行成功后,指标会直接显示在 Android Studio 中,包含额外指标和设备信息的完整基准报告以 JSON 格式提供。androidx.benchmark Gradle 插件默认会启用 JSON 输出。

微基准结果

默认情况下,JSON 报告会写入设备端磁盘的测试 APK 的外部共享媒体文件夹中,该文件夹通常位于 /storage/emulated/0/Android/media/app_id/app_id-benchmarkData.json

系统还会将 JSON 报告自动从设备复制到主机。这些报告将写入主机上的以下位置:

project_root/module/build/outputs/connected_android_test_additional_output/debugAndroidTest/connected/device_id/app_id-benchmarkData.json

配置错误

基准库会检测以下条件是否得到满足,确保项目和环境设置达到发布性能:

  • Debuggable 设置为 false
  • 正在使用的是实体设备(不支持模拟器)。
  • 如果设备启用了 root 权限,时钟已被锁定。
  • 设备的电池电量充足(至少 25%)。

如果上述任一项检查失败,基准将报告错误以避免不准确的测量结果。

如需抑制显示为警告的特定错误类型,同时阻止它们中止基准测试,请将相应错误类型以逗号分隔列表的形式传递给插桩参数 androidx.benchmark.suppressErrors

您可以通过 Gradle 脚本对此进行设置,如下所示:

Groovy

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

Kotlin

android {
    defaultConfig {
       …
      testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "DEBUGGABLE,LOW-BATTERY"
    }
}

抑制错误可让您在配置有误的状态下运行基准,并会导致系统刻意重命名基准的输出(具体方式为在测试名称前附加错误类型)。例如,如果通过上述代码段中的抑制操作运行可调试基准,系统会在测试名称前附加 DEBUGGABLE_