blob: 97cad8333d98c005bd7c57e81bcb714e0d4c9d85 [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
Ian Baker186108e2023-11-20 06:54:36 -080012MACRObenchmarks [here](/docs/macrobenchmarking.md).
AndroidX Core Team0e7745f2021-04-08 17:00:10 +000013
AndroidX Core Team2e416b22020-12-03 22:58:07 +000014### Writing the benchmark
15
16Benchmarks are just regular instrumentation tests! Just use the
AndroidX Core Team5330eef2023-02-21 16:07:59 -050017[`BenchmarkRule`](https://developer.android.com/reference/kotlin/androidx/benchmark/junit4/BenchmarkRule)
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
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -080079Start by copying one of the following non-Compose projects:
AndroidX Core Team2e416b22020-12-03 22:58:07 +000080
AndroidX Core Team5330eef2023-02-21 16:07:59 -050081* [navigation-benchmark](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-benchmark/)
82* [recyclerview-benchmark](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:recyclerview/recyclerview-benchmark/)
AndroidX Core Team2e416b22020-12-03 22:58:07 +000083
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -080084Many Compose libraries already have benchmark modules:
AndroidX Core Team2e416b22020-12-03 22:58:07 +000085
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -080086* [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 Team2e416b22020-12-03 22:58:07 +000090
91## Profiling
92
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -080093See the
94[public profiling guide](https://developer.android.com/studio/profile/benchmark#profiling)
95for more details.
AndroidX Core Team2e416b22020-12-03 22:58:07 +000096
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -080097Jetpack benchmark supports capturing profiling information by setting
98instrumentation arguments. Stack sampling and method tracing can be performed
99either from CLI or Studio invocation.
100
101### Set Arguments in Gradle
102
103Args can be set in your benchmark's `build.gradle`, which will affect both
104Studio / command-line gradlew runs. Runs from Studio will link result traces
105that can be opened directly from the IDE.
106
107```
108android {
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
118Args can also be passed from CLI. Here's an example which runs the
AndroidX Core Team5f312b62021-08-05 15:59:15 -0700119`androidx.compose.material.benchmark.CheckboxesInRowsBenchmark#draw` method with
120`StackSampling` profiling:
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000121
122```
AndroidX Core Team5f312b62021-08-05 15:59:15 -0700123./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 Team2e416b22020-12-03 22:58:07 +0000126```
127
128The command output will tell you where to look for the file on your host
129machine:
130
131```
13204:33:49 I/Benchmark: Benchmark report files generated at
AndroidX Core Team408c27b2020-12-15 15:57:00 +0000133/androidx-main/out/ui/ui/integration-tests/benchmark/build/outputs/connected_android_test_additional_output
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000134```
135
136To inspect the captured trace, open the appropriate `*.trace` file in that
137directory with Android Studio, using `File > Open`.
138
AndroidX Core Team5f312b62021-08-05 15:59:15 -0700139NOTE For stack sampling, it's recommended to profile on Android Q(API 29) or
140higher, as this enables the benchmark library to use
AndroidX Core Team5330eef2023-02-21 16:07:59 -0500141[Simpleperf](https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/)
142when capturing samples.
AndroidX Core Team5f312b62021-08-05 15:59:15 -0700143
144For more information on the `StackSampling` and `MethodTracing` profiling modes,
145see the
AndroidX Core Team5330eef2023-02-21 16:07:59 -0500146[Studio Profiler recording configuration docs](https://developer.android.com/studio/profile/record-traces#configurations),
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700147specifically "Sample C/C++ Functions" (called "Callstack sample" in recent
148versions), and Java Method Tracing.
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000149
150![Sample flame chart](benchmarking_images/profiling_flame_chart.png "Sample flame chart")
151
AndroidX Core Teamd9b6ba12022-02-08 10:58:40 -0800152### Advanced: Connected Studio Profiler
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000153
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700154Profiling for allocations requires Studio to capture, and a debuggable build. Do
155not commit the following changes.
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000156
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700157First, set your benchmark to be debuggable in your benchmark module's
158`androidTest/AndroidManifest.xml`:
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000159
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700160```
161 <application
162 ...
163 android:debuggable="false"
164 tools:ignore="HardcodedDebugMode"/>
165```
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000166
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700167Note that switching to the debug variant will likely not work, as Studio will
168fail to find the benchmark as a test source.
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000169
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700170Next select `ConnectedAllocation` in your benchmark module's `build.gradle`:
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000171
172```
173android {
174 defaultConfig {
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700175 // --- Local only, don't commit this! ---
176 // pause for manual profiler connection before/after a single run of
177 // the benchmark loop, after warmup
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000178 testInstrumentationRunnerArgument 'androidx.benchmark.profiling.mode', 'ConnectedAllocation'
179 }
180}
181```
182
183Run `File > Sync Project with Gradle Files`, or sync if Studio asks you. Now any
184benchmark runs in that project will permit debuggable, and pause before and
185after the test, to allow you to connect a profiler and start recording, and then
186stop recording.
187
188#### Running and Profiling
189
190After the benchmark test starts, you have about 20 seconds to connect the
191profiler:
192
1931. Click the profiler tab at the bottom
1941. Click the plus button in the top left, `<device name>`, `<process name>`
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -07001951. Click the memory section, and right click the window, and select `Record
196 allocations`.
1971. Approximately 20 seconds later, right click again and select `Stop
198 recording`.
AndroidX Core Team2e416b22020-12-03 22:58:07 +0000199
AndroidX Core Team2eeefeb2023-10-02 23:16:12 -0700200If timed correctly, you'll have started and stopped collection around the single
201run of your benchmark loop, and see all allocations in detail with call stacks
202in Studio.