编写微基准测试

要了解如何通过向应用程序代码添加更改来使用 Microbenchmark 库,请参阅快速入门部分。要了解如何通过对代码库进行更复杂的更改来完成完整的设置,请参阅完整项目设置部分。

快速入门

本节介绍如何在无需将代码移至模块的情况下试用基准测试和运行一次性测量。为了获得准确的性能结果,这些步骤需要在您的应用中禁用调试,因此请将其保存在本地工作副本中,而不要将更改提交到您的源代码管理系统。

要执行一次性基准测试,请执行以下操作

  1. 将库添加到模块的build.gradlebuild.gradle.kts文件中

    Kotlin

    dependencies {
        implementation("androidx.benchmark:benchmark-junit4:1.2.4")
    }

    Groovy

    dependencies {
        implementation 'androidx.benchmark:benchmark-junit4:1.2.4'
    }

    使用implementation依赖项而不是androidTestImplementation依赖项。如果您使用androidTestImplementation,基准测试将无法运行,因为库清单不会合并到应用清单中。

  2. 更新debug构建类型,使其不可调试

    Kotlin

    android {
        ...
        buildTypes {
            debug {
                isDebuggable = false
            }
        }
    }

    Groovy

    android {
        ...
        buildTypes {
            debug {
                debuggable false
            }
        }
    }
  3. testInstrumentationRunner更改为AndroidBenchmarkRunner

    Kotlin

    android {
        ...
        defaultConfig {
            testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }

    Groovy

    android {
        ...
        defaultConfig {
            testInstrumentationRunner "androidx.benchmark.junit4.AndroidBenchmarkRunner"
        }
    }
  4. androidTest目录下的测试文件中添加BenchmarkRule的实例来添加基准测试。有关编写基准测试的更多信息,请参阅创建微基准测试类

    以下代码片段显示了如何将基准测试添加到已 instrumentation 的测试中。

    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() {
                BenchmarkRuleKt.measureRepeated(
                    (Function1<BenchmarkRule.Scope, Unit>) scope -> doSomeWork()
                );
           }
        }
    }

要了解如何编写基准测试,请跳至创建微基准测试类

完整项目设置

要设置常规基准测试而不是一次性基准测试,请将基准测试隔离到它们自己的模块中。这有助于确保它们的配置(例如,将debuggable设置为false)与常规测试分开。

因为微基准测试直接运行您的代码,所以请将您想要进行基准测试的代码放在单独的 Gradle 模块中,并像图 1 所示那样设置对该模块的依赖关系。

app structure
图 1.带有:app:microbenchmark:benchmarkable Gradle 模块的应用程序结构,这允许微基准测试对:benchmarkable模块中的代码进行基准测试。

要添加新的 Gradle 模块,您可以使用 Android Studio 中的模块向导。向导会创建一个预配置用于基准测试的模块,其中添加了基准测试目录并将debuggable设置为false

  1. 在 Android Studio 的项目面板中右键单击您的项目或模块,然后单击新建 > 模块

  2. 模板窗格中选择基准测试

  3. 选择微基准测试作为基准测试模块类型。

  4. 为模块名称键入“microbenchmark”。

  5. 单击完成

Configure new library module
图 2.在 Android Studio Bumblebee 中添加新的 Gradle 模块。

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

Kotlin

dependencies {
    // The module name might be different.
    androidTestImplementation(project(":benchmarkable"))
}

Groovy

dependencies {
    // The module name might be different.
    androidTestImplementation project(':benchmarkable')
}

创建微基准测试类

基准测试是标准的 instrumentation 测试。要创建基准测试,请使用库提供的BenchmarkRule类。要对活动进行基准测试,请使用ActivityScenarioActivityScenarioRule。要对 UI 代码进行基准测试,请使用@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() {
    // Use random with the same seed, so that it generates the same data every
    // run.
    Random random = new Random(0);

    // Create the array once and 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一样运行基准测试,使用测试类或方法旁边的槽位操作,如图 3 所示。

Run Microbenchmark
图 3.使用测试类旁边的槽位操作运行微基准测试。

或者,从命令行运行connectedCheck以运行指定 Gradle 模块中的所有测试。

./gradlew benchmark:connectedCheck

或单个测试。

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

基准测试结果

微基准测试成功运行后,指标会直接显示在 Android Studio 中,并且完整的基准测试报告(包含其他指标和设备信息)以 JSON 格式提供。

Microbenchmark results
图 4.微基准测试结果。

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

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

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

配置错误

库检测以下条件以确保您的项目和环境已设置为发布版准确的性能:

  • 可调试设置为false
  • 正在使用物理设备——不支持模拟器。
  • 如果设备已 root,则时钟已锁定。
  • 设备上的电池电量充足,至少为 25%。

如果上述任何检查失败,基准测试会报告错误以避免不准确的测量。

要将特定错误类型抑制为警告并阻止它们停止基准测试,请将错误类型以逗号分隔的列表形式传递给 instrumentation 参数androidx.benchmark.suppressErrors

您可以从 Gradle 脚本中设置此参数,如下例所示。

Kotlin

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

Groovy

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

您也可以从命令行抑制错误。

$ ./gradlew :benchmark:connectedCheck -P andoidtestInstrumentationRunnerArguments.androidx.benchmark.supperssErrors=DEBUGGABLE,LOW-BATTERY

抑制错误允许基准测试在配置不正确的状态下运行,并且基准测试的输出会故意通过在测试名称前添加错误来重命名。例如,使用上述代码段中的抑制运行可调试基准测试会在测试名称前添加DEBUGGABLE_