Android NDK开发详解之调试和性能分析的编写 Microbenchmark

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

快速入门

本部分将介绍如何在无需将代码移到模块中的情况下,尝试执行基准测试以及执行一次性测量。为了获得准确的性能结果,本文中的步骤涉及在应用中停用调试功能,因此请将相关操作结果保留在本地工作副本中,而无需将更改提交至源代码控制系统中。

如需开展一次性基准测试,请执行以下操作:

将该库添加到您模块的 build.gradle 或 build.gradle.kts 文件中:

project_root/module_dir/build.gradle.kts

Kotlin
dependencies {
    androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.1")
}

Groovy
dependencies {
    androidTestImplementation("androidx.benchmark:benchmark-junit4:1.1.1")
}

更新 元素以暂时在测试清单中强制停用调试功能:

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"/>

在 androidTest 目录下的测试文件中添加 BenchmarkRule 的实例,以添加您的基准。如需详细了解如何编写基准,请参阅创建 Microbenchmark 类。

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

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

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

Java

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

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

如需了解如何编写基准,请跳至创建 Microbenchmark 类。

完整的项目设置

如需设置常规基准测试而非一次性基准测试,请将基准分隔到专门的模块中。这有助于确保其配置(例如将 debuggable 设置为 false)与常规测试分开。
注意:如果您未将您的测试和基准分离开,且为模块设置了 debuggable=false,则将无法在测试中使用调试程序和性能分析工具。

由于 Microbenchmark 会直接运行您的代码,因此请将要进行基准测试的代码放在单独的 Gradle 模块中,并设置对该模块的依赖项,如图 1 所示。
在这里插入图片描述

图 1. 包含 :app、:microbenchmark 和 :benchmarkable Gradle 模块的应用结构,允许 Microbenchmark 对 :benchmarkable 模块中的代码进行基准测试。

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

1、在 Android Studio 的 Project 面板中右键点击您的项目或模块,然后依次点击 New > Module。
2、在 Templates 窗格中选择 Benchmark。

3、选择 Microbenchmark 作为基准模块类型。

4、输入“microbenchmark”作为模块名称。

5、点击 Finish。

在这里插入图片描述

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

创建模块后,更改其 build.gradle 或 build.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"))
}

创建 Microbenchmark 类

基准是标准的插桩测试。如需创建基准,请使用基准库提供的 BenchmarkRule 类。如需对 activity 进行基准测试,请使用 ActivityScenario 或 ActivityScenarioRule。如需对界面代码进行基准测试,请使用 @UiThreadTest。

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

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

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

Java

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

    @Test
    fun benchmarkSomeWork() {
        benchmarkRule.measureRepeated {
            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


// 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)
}

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

运行基准

在 Android Studio 中,像使用测试类或方法旁边的边线操作运行任何 @Test 一样运行您的基准,如图 3 所示。
在这里插入图片描述

图 3. 使用测试类旁边的边线操作运行 Microbenchmark 测试。

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

./gradlew benchmark:connectedCheck

或单个测试:

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

基准结果

Microbenchmark 运行成功后,指标会直接显示在 Android Studio 中,包含额外指标和设备信息的完整基准报告还会以 JSON 格式提供。
在这里插入图片描述

图 4. Microbenchmark 结果。

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。

配置错误

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

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

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

如需抑制显示为警告的特定错误类型,同时阻止它们中止基准测试,请将相应错误类型以逗号分隔列表的形式传递给插桩参数 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_。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值