blob: a4fabb1191e7ab6e692b2513a60ac34e8bf1d1d6 [file] [log] [blame] [view]
AndroidX Core Team2e416b22020-12-03 22:58:07 +00001# Benchmarking in AndroidX
2
3[TOC]
4
5The public documentation at
6[d.android.com/benchmark](http://d.android.com/benchmark) explains how to use
7the library - this page focuses on specifics to writing libraries in the
8AndroidX repo, and our continuous testing / triage process.
9
AndroidX Core Team0e7745f2021-04-08 17:00:10 +000010This page is for MICRO benchmarks measuring CPU performance of small sections of
11code. If you're looking for measuring startup or jank, see the guide for
12MACRObenchmarks [here](macrobenchmarking).
13
AndroidX Core Team2e416b22020-12-03 22:58:07 +000014### Writing the benchmark
15
16Benchmarks are just regular instrumentation tests! Just use the
AndroidX Core Team408c27b2020-12-15 15:57:00 +000017[`BenchmarkRule`](https://android.googlesource.com/platform/frameworks/support/+/androidx-main/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt)
AndroidX Core Team2e416b22020-12-03 22:58:07 +000018provided by the library:
19
20<section class="tabs">
21
22#### Kotlin {.new-tab}
23
24```kotlin
25@RunWith(AndroidJUnit4::class)
26class 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)
48public 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
71As in the public documentation, benchmarks in the AndroidX repo are test-only
72library modules. Differences for AndroidX repo:
73
AndroidX Core Team0e7745f2021-04-08 17:00:10 +0000741. Module must live in `integration-tests` group directory
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000751. Module name must end with `-benchmark` in `settings.gradle`.
AndroidX Core Team2e416b22020-12-03 22:58:07 +000076
77### I'm lazy and want to start quickly
78
79Start by copying one of the following projects:
80
AndroidX Core Team408c27b2020-12-15 15:57:00 +000081* [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 Team2e416b22020-12-03 22:58:07 +000083
84### Compose
85
86Compose builds the benchmark from source, so usage matches the rest of the
87AndroidX project. See existing Compose benchmark projects:
88
AndroidX Core Team408c27b2020-12-15 15:57:00 +000089* [Compose UI benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/integration-tests/benchmark/)
90* [Compose Runtime benchmarks](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/runtime/runtime/compose-runtime-benchmark/)
AndroidX Core Team2e416b22020-12-03 22:58:07 +000091
92## Profiling
93
94### Command Line
95
96The benchmark library supports capturing profiling information - sampled and
97method - from the command line. Here's an example which runs the
98`androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw` method with
99`MethodSampling` profiling:
100
101```
102./gradlew compose:integ:bench:cC \
103 -P android.testInstrumentationRunnerArguments.androidx.benchmark.profiling.mode=MethodSampling \
104 -P android.testInstrumentationRunnerArguments.class=androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw
105```
106
107The command output will tell you where to look for the file on your host
108machine:
109
110```
11104:33:49 I/Benchmark: Benchmark report files generated at
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000112/androidx-main/out/ui/ui/integration-tests/benchmark/build/outputs/connected_android_test_additional_output
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000113```
114
115To inspect the captured trace, open the appropriate `*.trace` file in that
116directory with Android Studio, using `File > Open`.
117
118For more information on the `MethodSampling` and `MethodTracing` profiling
119modes, see the
120[Studio Profiler configuration docs](https://developer.android.com/studio/profile/cpu-profiler#configurations),
121specifically Java Sampled Profiling, and Java Method Tracing.
122
123![Sample flame chart](benchmarking_images/profiling_flame_chart.png "Sample flame chart")
124
125### Advanced: Simpleperf Method Sampling
126
127[Simpleperf](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/)
128offers more accurate profiling for apps than standard method sampling, due to
129lower overhead (as well as C++ profiling support). Simpleperf support will be
130simplified and improved over time.
131
132[Simpleperf app profiling docs](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md).
133
134#### Device
135
AndroidX Core Team0db91f02021-05-06 22:45:18 +0000136Get an API 29+ device. The rest of this section is about *why* those constraints
137exist, skip if not interested.
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000138
139Simpleperf has restrictions about where it can be used - Jetpack Benchmark will
AndroidX Core Team0db91f02021-05-06 22:45:18 +0000140only support API 29+ for now, due to
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000141[platform/simpleperf constraints](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md#prepare-an-android-application)
142(see last subsection titled "If you want to profile Java code"). Summary is:
143
144- <=23 (M): Unsupported for Java code.
145
146- 24-25 (N): Requires compiled Java code. We haven't investigated support.
147
148- 26 (O): Requires compiled Java code, and wrapper script. We haven't
149 investigated support.
150
AndroidX Core Team0db91f02021-05-06 22:45:18 +0000151- 27 (O.1): Can profile all Java code, but requires `userdebug`/rooted device
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000152
AndroidX Core Team0db91f02021-05-06 22:45:18 +0000153- 28 (P): Can profile all Java code, requires debuggable (or
154 `userdebug`/rooted device, but this appears to not be supported by scripts
155 currently)
156
157- \>=29 (Q): Can profile all Java code, requires profileable or debuggable (or
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000158 `userdebug`/rooted device)
159
160We aren't planning to support profiling debuggable APK builds, since they're
161misleading for profiling.
162
163#### Initial setup
164
165Currently, we rely on Python scripts built by the simpleperf team. We can
166eventually build this into the benchmark library / gradle plugin. Download the
167scripts from AOSP:
168
169```
170# copying to somewhere outside of the androidx repo
171git clone https://android.googlesource.com/platform/system/extras ~/simpleperf
172```
173
174Next configure your path to ensure the ADB that the scripts will use matches the
175androidx tools:
176
177```
178export PATH=$PATH:<path/to/androidx>/prebuilts/fullsdk-<linux or darwin>/platform-tools
179```
180
181Now, setup your device for simpleperf:
182
183```
184~/simpleperf/simpleperf/scripts/api_profiler.py prepare --max-sample-rate 10000000
185```
186
187#### Build and Run, Option 1: Studio (slightly recommended)
188
189Running from Studio is simpler, since you don't have to manually install and run
190the APKs, avoiding Gradle.
191
192Add the following to the benchmark module's build.gradle:
193
194```
195android {
196 defaultConfig {
197 // DO NOT COMMIT!!
198 testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'MethodSamplingSimpleperf'
199 // Optional: Control freq / duration.
200 testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleFrequency', '1000000'
201 testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleDurationSeconds', '5'
202 }
203}
204```
205
206And run the test or tests you'd like to measure from within Studio.
207
208#### Build and Run, Option 2: Command Line
209
210**Note - this will be significantly simplified in the future**
211
212Since we're not using AGP to pull the files yet, we can't invoke the benchmark
213through Gradle, because Gradle uninstalls after each test run. Instead, let's
214just build and run manually:
215
216```
217./gradlew compose:integration-tests:benchmark:assembleReleaseAndroidTest
218
219adb install -r ../../../out/ui/compose/integration-tests/benchmark/build/outputs/apk/androidTest/release/benchmark-release-androidTest.apk
220
221# run the test (can copy this line from Studio console, when running a benchmark)
222adb shell am instrument -w -m --no-window-animation -e androidx.benchmark.profiling.mode MethodSamplingSimpleperf -e debug false -e class 'androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#toggleCheckbox_draw' androidx.ui.benchmark.test/androidx.benchmark.junit4.AndroidBenchmarkRunner
223```
224
225#### Pull and open the trace
226
227```
228# move the files to host
229# (Note: removes files from device)
230~/simpleperf/simpleperf/scripts/api_profiler.py collect -p androidx.ui.benchmark.test -o ~/simpleperf/results
231
232# create/open the HTML report
233~/simpleperf/simpleperf/scripts/report_html.py -i ~/simpleperf/results/CheckboxesInRowsBenchmark_toggleCheckbox_draw\[1\].data
234```
235
236### Advanced: Studio Profiling
237
238Profiling for allocations and simpleperf profiling requires Studio to capture.
239
240Studio profiling tools require `debuggable=true`. First, temporarily override it
241in your benchmark's `androidTest/AndroidManifest.xml`.
242
243Next choose which profiling you want to do: Allocation, or Sampled (SimplePerf)
244
245`ConnectedAllocation` will help you measure the allocations in a single run of a
246benchmark loop, after warmup.
247
248`ConnectedSampled` will help you capture sampled profiling, but with the more
249detailed / accurate Simpleperf sampling.
250
251Set the profiling type in your benchmark module's `build.gradle`:
252
253```
254android {
255 defaultConfig {
256 // Local only, don't commit this!
257 testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'ConnectedAllocation'
258 }
259}
260```
261
262Run `File > Sync Project with Gradle Files`, or sync if Studio asks you. Now any
263benchmark runs in that project will permit debuggable, and pause before and
264after the test, to allow you to connect a profiler and start recording, and then
265stop recording.
266
267#### Running and Profiling
268
269After the benchmark test starts, you have about 20 seconds to connect the
270profiler:
271
2721. Click the profiler tab at the bottom
2731. Click the plus button in the top left, `<device name>`, `<process name>`
2741. Next step depends on which you intend to capture
275
276#### Allocations
277
278Click the memory section, and right click the window, and select `Record
279allocations`. Approximately 20 seconds later, right click again and select `Stop
280recording`.