blob: cc38f78ebf42e6dc4602027b70942fcb262cd0c5 [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
10### Writing the benchmark
11
12Benchmarks are just regular instrumentation tests! Just use the
13[`BenchmarkRule`](https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/benchmark/junit4/src/main/java/androidx/benchmark/junit4/BenchmarkRule.kt)
14provided by the library:
15
16<section class="tabs">
17
18#### Kotlin {.new-tab}
19
20```kotlin
21@RunWith(AndroidJUnit4::class)
22class ViewBenchmark {
23 @get:Rule
24 val benchmarkRule = BenchmarkRule()
25
26 @Test
27 fun simpleViewInflate() {
28 val context = InstrumentationRegistry
29 .getInstrumentation().targetContext
30 val inflater = LayoutInflater.from(context)
31 val root = FrameLayout(context)
32
33 benchmarkRule.measure {
34 inflater.inflate(R.layout.test_simple_view, root, false)
35 }
36 }
37}
38```
39
40#### Java {.new-tab}
41
42```java
43@RunWith(AndroidJUnit4.class)
44public class ViewBenchmark {
45 @Rule
46 public BenchmarkRule mBenchmarkRule = new BenchmarkRule();
47
48 @Test
49 public void simpleViewInflate() {
50 Context context = InstrumentationRegistry
51 .getInstrumentation().getTargetContext();
52 final BenchmarkState state = mBenchmarkRule.getState();
53 LayoutInflater inflater = LayoutInflater.from(context);
54 FrameLayout root = new FrameLayout(context);
55
56 while (state.keepRunning()) {
57 inflater.inflate(R.layout.test_simple_view, root, false);
58 }
59 }
60}
61```
62
63</section>
64
65## Project structure
66
67As in the public documentation, benchmarks in the AndroidX repo are test-only
68library modules. Differences for AndroidX repo:
69
701. Module name must end with `-benchmark` in `settings.gradle`.
712. You do not need to apply the benchmark plugin (it's pulled in automatically
72 from source)
73
74### I'm lazy and want to start quickly
75
76Start by copying one of the following projects:
77
78* [navigation-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/navigation/benchmark/)
79* [recyclerview-benchmark](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/recyclerview/recyclerview-benchmark/)
80
81### Compose
82
83Compose builds the benchmark from source, so usage matches the rest of the
84AndroidX project. See existing Compose benchmark projects:
85
86* [Compose UI benchmarks](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/ui/integration-tests/benchmark/)
87* [Compose Runtime benchmarks](https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/compose/compose-runtime/compose-runtime-benchmark/)
88
89## Profiling
90
91### Command Line
92
93The benchmark library supports capturing profiling information - sampled and
94method - from the command line. Here's an example which runs the
95`androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw` method with
96`MethodSampling` profiling:
97
98```
99./gradlew compose:integ:bench:cC \
100 -P android.testInstrumentationRunnerArguments.androidx.benchmark.profiling.mode=MethodSampling \
101 -P android.testInstrumentationRunnerArguments.class=androidx.ui.benchmark.test.CheckboxesInRowsBenchmark#draw
102```
103
104The command output will tell you where to look for the file on your host
105machine:
106
107```
10804:33:49 I/Benchmark: Benchmark report files generated at
109/androidx-master-dev/out/ui/ui/integration-tests/benchmark/build/outputs/connected_android_test_additional_output
110```
111
112To inspect the captured trace, open the appropriate `*.trace` file in that
113directory with Android Studio, using `File > Open`.
114
115For more information on the `MethodSampling` and `MethodTracing` profiling
116modes, see the
117[Studio Profiler configuration docs](https://developer.android.com/studio/profile/cpu-profiler#configurations),
118specifically Java Sampled Profiling, and Java Method Tracing.
119
120![Sample flame chart](benchmarking_images/profiling_flame_chart.png "Sample flame chart")
121
122### Advanced: Simpleperf Method Sampling
123
124[Simpleperf](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/)
125offers more accurate profiling for apps than standard method sampling, due to
126lower overhead (as well as C++ profiling support). Simpleperf support will be
127simplified and improved over time.
128
129[Simpleperf app profiling docs](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md).
130
131#### Device
132
133Get an API 28+ device (Or a rooted API 27 device). The rest of this section is
134about *why* those constraints exist, skip if not interested.
135
136Simpleperf has restrictions about where it can be used - Jetpack Benchmark will
137only support API 28+ for now, due to
138[platform/simpleperf constraints](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/android_application_profiling.md#prepare-an-android-application)
139(see last subsection titled "If you want to profile Java code"). Summary is:
140
141- <=23 (M): Unsupported for Java code.
142
143- 24-25 (N): Requires compiled Java code. We haven't investigated support.
144
145- 26 (O): Requires compiled Java code, and wrapper script. We haven't
146 investigated support.
147
148- 27 (P): Can profile all Java code, but requires `userdebug`/rooted device
149
150- \>=28 (Q): Can profile all Java code, requires profileable (or
151 `userdebug`/rooted device)
152
153We aren't planning to support profiling debuggable APK builds, since they're
154misleading for profiling.
155
156#### Initial setup
157
158Currently, we rely on Python scripts built by the simpleperf team. We can
159eventually build this into the benchmark library / gradle plugin. Download the
160scripts from AOSP:
161
162```
163# copying to somewhere outside of the androidx repo
164git clone https://android.googlesource.com/platform/system/extras ~/simpleperf
165```
166
167Next configure your path to ensure the ADB that the scripts will use matches the
168androidx tools:
169
170```
171export PATH=$PATH:<path/to/androidx>/prebuilts/fullsdk-<linux or darwin>/platform-tools
172```
173
174Now, setup your device for simpleperf:
175
176```
177~/simpleperf/simpleperf/scripts/api_profiler.py prepare --max-sample-rate 10000000
178```
179
180#### Build and Run, Option 1: Studio (slightly recommended)
181
182Running from Studio is simpler, since you don't have to manually install and run
183the APKs, avoiding Gradle.
184
185Add the following to the benchmark module's build.gradle:
186
187```
188android {
189 defaultConfig {
190 // DO NOT COMMIT!!
191 testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'MethodSamplingSimpleperf'
192 // Optional: Control freq / duration.
193 testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleFrequency', '1000000'
194 testInstrumentationRunnerArgument 'androidx.benchmark.profiler.sampleDurationSeconds', '5'
195 }
196}
197```
198
199And run the test or tests you'd like to measure from within Studio.
200
201#### Build and Run, Option 2: Command Line
202
203**Note - this will be significantly simplified in the future**
204
205Since we're not using AGP to pull the files yet, we can't invoke the benchmark
206through Gradle, because Gradle uninstalls after each test run. Instead, let's
207just build and run manually:
208
209```
210./gradlew compose:integration-tests:benchmark:assembleReleaseAndroidTest
211
212adb install -r ../../../out/ui/compose/integration-tests/benchmark/build/outputs/apk/androidTest/release/benchmark-release-androidTest.apk
213
214# run the test (can copy this line from Studio console, when running a benchmark)
215adb 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
216```
217
218#### Pull and open the trace
219
220```
221# move the files to host
222# (Note: removes files from device)
223~/simpleperf/simpleperf/scripts/api_profiler.py collect -p androidx.ui.benchmark.test -o ~/simpleperf/results
224
225# create/open the HTML report
226~/simpleperf/simpleperf/scripts/report_html.py -i ~/simpleperf/results/CheckboxesInRowsBenchmark_toggleCheckbox_draw\[1\].data
227```
228
229### Advanced: Studio Profiling
230
231Profiling for allocations and simpleperf profiling requires Studio to capture.
232
233Studio profiling tools require `debuggable=true`. First, temporarily override it
234in your benchmark's `androidTest/AndroidManifest.xml`.
235
236Next choose which profiling you want to do: Allocation, or Sampled (SimplePerf)
237
238`ConnectedAllocation` will help you measure the allocations in a single run of a
239benchmark loop, after warmup.
240
241`ConnectedSampled` will help you capture sampled profiling, but with the more
242detailed / accurate Simpleperf sampling.
243
244Set the profiling type in your benchmark module's `build.gradle`:
245
246```
247android {
248 defaultConfig {
249 // Local only, don't commit this!
250 testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'ConnectedAllocation'
251 }
252}
253```
254
255Run `File > Sync Project with Gradle Files`, or sync if Studio asks you. Now any
256benchmark runs in that project will permit debuggable, and pause before and
257after the test, to allow you to connect a profiler and start recording, and then
258stop recording.
259
260#### Running and Profiling
261
262After the benchmark test starts, you have about 20 seconds to connect the
263profiler:
264
2651. Click the profiler tab at the bottom
2661. Click the plus button in the top left, `<device name>`, `<process name>`
2671. Next step depends on which you intend to capture
268
269#### Allocations
270
271Click the memory section, and right click the window, and select `Record
272allocations`. Approximately 20 seconds later, right click again and select `Stop
273recording`.