AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 1 | # Benchmarking in AndroidX |
| 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | The public documentation at |
| 6 | [d.android.com/benchmark](http://d.android.com/benchmark) explains how to use |
| 7 | the library - this page focuses on specifics to writing libraries in the |
| 8 | AndroidX repo, and our continuous testing / triage process. |
| 9 | |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 10 | This page is for MICRO benchmarks measuring CPU performance of small sections of |
| 11 | code. If you're looking for measuring startup or jank, see the guide for |
| 12 | MACRObenchmarks [here](macrobenchmarking). |
| 13 | |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 14 | ### Writing the benchmark |
| 15 | |
| 16 | Benchmarks are just regular instrumentation tests! Just use the |
AndroidX Core Team | 408c27b | 2020-12-15 15:57:00 +0000 | [diff] [blame] | 17 | [`BenchmarkRule`](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt) |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 18 | provided by the library: |
| 19 | |
| 20 | <section class="tabs"> |
| 21 | |
| 22 | #### Kotlin {.new-tab} |
| 23 | |
| 24 | ```kotlin |
| 25 | @RunWith(AndroidJUnit4::class) |
| 26 | class ViewBenchmark { |
| 27 | @get:Rule |
| 28 | val benchmarkRule = BenchmarkRule() |
| 29 | |
| 30 | @Test |
| 31 | fun simpleViewInflate() { |
| 32 | val context = InstrumentationRegistry |
| 33 | .getInstrumentation().targetContext |
| 34 | val inflater = LayoutInflater.from(context) |
| 35 | val root = FrameLayout(context) |
| 36 | |
| 37 | benchmarkRule.measure { |
| 38 | inflater.inflate(R.layout.test_simple_view, root, false) |
| 39 | } |
| 40 | } |
| 41 | } |
| 42 | ``` |
| 43 | |
| 44 | #### Java {.new-tab} |
| 45 | |
| 46 | ```java |
| 47 | @RunWith(AndroidJUnit4.class) |
| 48 | public class ViewBenchmark { |
| 49 | @Rule |
| 50 | public BenchmarkRule mBenchmarkRule = new BenchmarkRule(); |
| 51 | |
| 52 | @Test |
| 53 | public void simpleViewInflate() { |
| 54 | Context context = InstrumentationRegistry |
| 55 | .getInstrumentation().getTargetContext(); |
| 56 | final BenchmarkState state = mBenchmarkRule.getState(); |
| 57 | LayoutInflater inflater = LayoutInflater.from(context); |
| 58 | FrameLayout root = new FrameLayout(context); |
| 59 | |
| 60 | while (state.keepRunning()) { |
| 61 | inflater.inflate(R.layout.test_simple_view, root, false); |
| 62 | } |
| 63 | } |
| 64 | } |
| 65 | ``` |
| 66 | |
| 67 | </section> |
| 68 | |
| 69 | ## Project structure |
| 70 | |
| 71 | As in the public documentation, benchmarks in the AndroidX repo are test-only |
| 72 | library modules. Differences for AndroidX repo: |
| 73 | |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 74 | 1. Module must live in `integration-tests` group directory |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 75 | 1. Module name must end with `-benchmark` in `settings.gradle`. |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 76 | |
| 77 | ### I'm lazy and want to start quickly |
| 78 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 79 | Start by copying one of the following non-Compose projects: |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 80 | |
AndroidX Core Team | 408c27b | 2020-12-15 15:57:00 +0000 | [diff] [blame] | 81 | * [navigation-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/navigation/benchmark/) |
| 82 | * [recyclerview-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-main/recyclerview/recyclerview-benchmark/) |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 83 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 84 | Many Compose libraries already have benchmark modules: |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 85 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 86 | * [Compose UI Benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/benchmark/) |
| 87 | * [Compose Runtime Benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/compose-runtime-benchmark/) |
| 88 | * [Compose Material Benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/benchmark/) |
| 89 | * [Wear Compose Material Benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:wear/compose/compose-material/benchmark/) |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 90 | |
| 91 | ## Profiling |
| 92 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 93 | See the |
| 94 | [public profiling guide](https://developer.android.com/studio/profile/benchmark#profiling) |
| 95 | for more details. |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 96 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 97 | Jetpack benchmark supports capturing profiling information by setting |
| 98 | instrumentation arguments. Stack sampling and method tracing can be performed |
| 99 | either from CLI or Studio invocation. |
| 100 | |
| 101 | ### Set Arguments in Gradle |
| 102 | |
| 103 | Args can be set in your benchmark's `build.gradle`, which will affect both |
| 104 | Studio / command-line gradlew runs. Runs from Studio will link result traces |
| 105 | that can be opened directly from the IDE. |
| 106 | |
| 107 | ``` |
| 108 | android { |
| 109 | defaultConfig { |
| 110 | // must be one of: 'None', 'StackSampling', or 'MethodTracing' |
| 111 | testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'StackSampling' |
| 112 | } |
| 113 | } |
| 114 | ``` |
| 115 | |
| 116 | ### Set Arguments on Command Line |
| 117 | |
| 118 | Args can also be passed from CLI. Here's an example which runs the |
AndroidX Core Team | 5f312b6 | 2021-08-05 15:59:15 -0700 | [diff] [blame] | 119 | `androidx.compose.material.benchmark.CheckboxesInRowsBenchmark#draw` method with |
| 120 | `StackSampling` profiling: |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 121 | |
| 122 | ``` |
AndroidX Core Team | 5f312b6 | 2021-08-05 15:59:15 -0700 | [diff] [blame] | 123 | ./gradlew compose:material:material-benchmark:cC \ |
| 124 | -P android.testInstrumentationRunnerArguments.androidx.benchmark.profiling.mode=StackSampling \ |
| 125 | -P android.testInstrumentationRunnerArguments.class=androidx.compose.material.benchmark.CheckboxesInRowsBenchmark#draw |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 126 | ``` |
| 127 | |
| 128 | The command output will tell you where to look for the file on your host |
| 129 | machine: |
| 130 | |
| 131 | ``` |
| 132 | 04:33:49 I/Benchmark: Benchmark report files generated at |
AndroidX Core Team | 408c27b | 2020-12-15 15:57:00 +0000 | [diff] [blame] | 133 | /androidx-main/out/ui/ui/integration-tests/benchmark/build/outputs/connected_android_test_additional_output |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 134 | ``` |
| 135 | |
| 136 | To inspect the captured trace, open the appropriate `*.trace` file in that |
| 137 | directory with Android Studio, using `File > Open`. |
| 138 | |
AndroidX Core Team | 5f312b6 | 2021-08-05 15:59:15 -0700 | [diff] [blame] | 139 | NOTE For stack sampling, it's recommended to profile on Android Q(API 29) or |
| 140 | higher, as this enables the benchmark library to use |
| 141 | [Simpleperf](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/). |
| 142 | Simpleperf previously required a |
| 143 | [more complex setup process](https://issuetracker.google.com/issues/158303822) - |
| 144 | this has been fixed! |
| 145 | |
| 146 | For more information on the `StackSampling` and `MethodTracing` profiling modes, |
| 147 | see the |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 148 | [Studio Profiler configuration docs](https://developer.android.com/studio/profile/cpu-profiler#configurations), |
AndroidX Core Team | 5f312b6 | 2021-08-05 15:59:15 -0700 | [diff] [blame] | 149 | specifically "Sample C/C++ Functions" (a confusing name for Simpleperf), and |
| 150 | Java Method Tracing. |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 151 | |
| 152 |  |
| 153 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 154 | ### Advanced: Connected Studio Profiler |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 155 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 156 | Profiling for allocations requires Studio to capture. This can also be used for |
| 157 | Sampled profiling, though it is instead recommended to use instrumentation |
| 158 | argument profiling for that, as it's simpler, and doesn't require |
| 159 | `debuggable=true` |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 160 | |
AndroidX Core Team | d9b6ba1 | 2022-02-08 10:58:40 -0800 | [diff] [blame] | 161 | Studio profiling tools currently require `debuggable=true`. First, temporarily |
| 162 | override it in your benchmark's `androidTest/AndroidManifest.xml`. |
AndroidX Core Team | 2e416b2 | 2020-12-03 22:58:07 +0000 | [diff] [blame] | 163 | |
| 164 | Next choose which profiling you want to do: Allocation, or Sampled (SimplePerf) |
| 165 | |
| 166 | `ConnectedAllocation` will help you measure the allocations in a single run of a |
| 167 | benchmark loop, after warmup. |
| 168 | |
| 169 | `ConnectedSampled` will help you capture sampled profiling, but with the more |
| 170 | detailed / accurate Simpleperf sampling. |
| 171 | |
| 172 | Set the profiling type in your benchmark module's `build.gradle`: |
| 173 | |
| 174 | ``` |
| 175 | android { |
| 176 | defaultConfig { |
| 177 | // Local only, don't commit this! |
| 178 | testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'ConnectedAllocation' |
| 179 | } |
| 180 | } |
| 181 | ``` |
| 182 | |
| 183 | Run `File > Sync Project with Gradle Files`, or sync if Studio asks you. Now any |
| 184 | benchmark runs in that project will permit debuggable, and pause before and |
| 185 | after the test, to allow you to connect a profiler and start recording, and then |
| 186 | stop recording. |
| 187 | |
| 188 | #### Running and Profiling |
| 189 | |
| 190 | After the benchmark test starts, you have about 20 seconds to connect the |
| 191 | profiler: |
| 192 | |
| 193 | 1. Click the profiler tab at the bottom |
| 194 | 1. Click the plus button in the top left, `<device name>`, `<process name>` |
| 195 | 1. Next step depends on which you intend to capture |
| 196 | |
| 197 | #### Allocations |
| 198 | |
| 199 | Click the memory section, and right click the window, and select `Record |
| 200 | allocations`. Approximately 20 seconds later, right click again and select `Stop |
| 201 | recording`. |