AndroidX Core Team | 3cc85e48 | 2024-04-15 09:30:13 -0700 | [diff] [blame] | 1 | # MACRObenchmarking in AndroidX |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 2 | |
| 3 | [TOC] |
| 4 | |
| 5 | <!-- Copied from macrobenchmark docs --> |
| 6 | |
| 7 | <table> |
| 8 | <tr> |
AndroidX Core Team | e615071 | 2023-08-03 11:06:01 -0700 | [diff] [blame] | 9 | <td><strong>Macrobenchmark</strong></td> |
| 10 | <td><strong>Benchmark</strong></td> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 11 | </tr> |
| 12 | <tr> |
AndroidX Core Team | 3cc85e48 | 2024-04-15 09:30:13 -0700 | [diff] [blame] | 13 | <td>Measure high-level entry points (Activity launch / Scrolling a list)</td> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 14 | <td>Measure individual functions</td> |
| 15 | </tr> |
| 16 | <tr> |
| 17 | <td>Out-of-process test of full app</td> |
| 18 | <td>In-process test of CPU work</td> |
| 19 | </tr> |
| 20 | <tr> |
AndroidX Core Team | 4cc85fa | 2021-11-23 15:58:34 +0000 | [diff] [blame] | 21 | <td>Slow iteration speed (Often more than a minute)</td> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 22 | <td>Fast iteration speed (Often less than 10 seconds)</td> |
| 23 | </tr> |
| 24 | <tr> |
AndroidX Core Team | 3cc85e48 | 2024-04-15 09:30:13 -0700 | [diff] [blame] | 25 | <td>Configure compilation with <a href="https://developer.android.com/reference/androidx/benchmark/macro/CompilationMode">CompilationMode</a></td> |
AndroidX Core Team | e615071 | 2023-08-03 11:06:01 -0700 | [diff] [blame] | 26 | <td>Always fully AOT (<code>speed</code>) compiled.</td> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 27 | </tr> |
| 28 | <tr> |
AndroidX Core Team | 4cc85fa | 2021-11-23 15:58:34 +0000 | [diff] [blame] | 29 | <td>Min API 23</td> |
AndroidX Core Team | 3cc85e48 | 2024-04-15 09:30:13 -0700 | [diff] [blame] | 30 | <td>Min API 19</td> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 31 | </tr> |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 32 | <tr> |
| 33 | <td>Relatively lower stability measurements</td> |
| 34 | <td>Higher stability measurements</td> |
| 35 | </tr> |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 36 | </table> |
| 37 | |
| 38 | The |
AndroidX Core Team | 9bd4c78 | 2021-05-19 14:20:12 -0700 | [diff] [blame] | 39 | [public documentation](https://developer.android.com/studio/profile/macrobenchmark) |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 40 | for macrobenchmark explains how to use the library. This page focuses on |
| 41 | specifics to writing library macrobenchmarks in the AndroidX repo. If you're |
| 42 | looking for measuring CPU perf of individual functions, see the guide for |
Ian Baker | 186108e | 2023-11-20 06:54:36 -0800 | [diff] [blame] | 43 | MICRObenchmarks [here](/docs/benchmarking.md). |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 44 | |
| 45 | ### Writing the benchmark |
| 46 | |
| 47 | Benchmarks are just regular instrumentation tests! Just use the |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 48 | [`MacrobenchmarkRule`](https://developer.android.com/reference/kotlin/androidx/benchmark/macro/junit4/MacrobenchmarkRule) |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 49 | provided by the library: |
| 50 | |
| 51 | <section class="tabs"> |
| 52 | |
| 53 | #### Kotlin {.new-tab} |
| 54 | |
| 55 | ```kotlin |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 56 | @get:Rule |
| 57 | val benchmarkRule = MacrobenchmarkRule() |
| 58 | |
| 59 | @Test |
| 60 | fun startup() = benchmarkRule.measureRepeated( |
| 61 | packageName = "mypackage.myapp", |
| 62 | metrics = listOf(StartupTimingMetric()), |
| 63 | startupMode = StartupMode.COLD, |
AndroidX Core Team | e615071 | 2023-08-03 11:06:01 -0700 | [diff] [blame] | 64 | iterations = 10 |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 65 | ) { // this = MacrobenchmarkScope |
| 66 | pressHome() |
| 67 | val intent = Intent() |
| 68 | intent.setPackage("mypackage.myapp") |
| 69 | intent.setAction("mypackage.myapp.myaction") |
| 70 | startActivityAndWait(intent) |
| 71 | } |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 72 | ``` |
| 73 | |
| 74 | #### Java {.new-tab} |
| 75 | |
| 76 | ```java |
AndroidX Core Team | 4cc85fa | 2021-11-23 15:58:34 +0000 | [diff] [blame] | 77 | @Rule |
| 78 | public MacrobenchmarkRule mBenchmarkRule = MacrobenchmarkRule() |
| 79 | |
| 80 | @Test |
| 81 | public void startup() { |
| 82 | mBenchmarkRule.measureRepeated( |
| 83 | "mypackage.myapp", |
| 84 | Collections.singletonList(new StartupTimingMetric()), |
| 85 | StartupMode.COLD, |
AndroidX Core Team | e615071 | 2023-08-03 11:06:01 -0700 | [diff] [blame] | 86 | /* iterations = */ 10, |
AndroidX Core Team | 4cc85fa | 2021-11-23 15:58:34 +0000 | [diff] [blame] | 87 | scope -> { |
| 88 | scope.pressHome(); |
| 89 | Intent intent = Intent(); |
| 90 | intent.setPackage("mypackage.myapp"); |
| 91 | intent.setAction("mypackage.myapp.myaction"); |
| 92 | scope.startActivityAndWait(intent); |
| 93 | return Unit.INSTANCE; |
| 94 | } |
| 95 | ); |
| 96 | } |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 97 | ``` |
| 98 | |
| 99 | </section> |
| 100 | |
| 101 | ## Project structure |
| 102 | |
| 103 | As in the public documentation, macrobenchmarks in the AndroidX repo are |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 104 | comprised of an app, and a separate macrobenchmark test module. In the AndroidX |
| 105 | repository, there are additional requirements: |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 106 | |
AndroidX Core Team | 11f74f7 | 2024-09-27 08:53:22 -0700 | [diff] [blame] | 107 | 1. Macrobenchmark test module path in `settings.gradle` **must** end with |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 108 | `macrobenchmark` to run in CI. |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 109 | |
AndroidX Core Team | 11f74f7 | 2024-09-27 08:53:22 -0700 | [diff] [blame] | 110 | 1. Macrobenchmark target module path in `settings.gradle` **should** end with |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 111 | `macrobenchmark-target` to follow convention. |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 112 | |
AndroidX Core Team | 11f74f7 | 2024-09-27 08:53:22 -0700 | [diff] [blame] | 113 | 1. Macrobenchmark modules **must** use project dependencies where available (so |
| 114 | `implementation(project(":activity:activity-ktx"))` rather than |
| 115 | `implementation("androidx.activity:activity-ktx:1.5.0")`). This prevents |
| 116 | accidentally testing against out-of-date versions, and increases coverage of |
| 117 | lower level libraries. |
| 118 | |
| 119 | 1. Each library group **must** declare its own in-group macrobenchmark test and |
| 120 | app module, with out using these modules for anything else (e.g. samples). |
| 121 | We want to be intentional about which changes affect measurements. More than |
| 122 | one of either is allowed, which is sometimes necessary to compare different |
| 123 | startup behaviors, see e.g. |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 124 | `:emoji2:integration-tests:init-<disabled/enabled>-macrobenchmark-target`. |
| 125 | Note that comparing multiple app variants are not currently supported by CI. |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 126 | |
| 127 | Compose Macrobenchmark Examples: |
| 128 | |
| 129 | * [`:compose:integration-tests:macrobenchmark-target`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/integration-tests/macrobenchmark-target/) |
| 130 | |
| 131 | * [`:compose:integration-tests:macrobenchmark`](https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/integration-tests/macrobenchmark/) |
| 132 | |
AndroidX Core Team | 3cc85e48 | 2024-04-15 09:30:13 -0700 | [diff] [blame] | 133 | Note: Compose macrobenchmarks are ideally duplicated with View system |
AndroidX Core Team | 0e7745f | 2021-04-08 17:00:10 +0000 | [diff] [blame] | 134 | counterparts, defined in `:benchmark:integration-tests:macrobenchmark-target`. |
| 135 | This is how we compare performance of the two systems. |
AndroidX Core Team | 6146a74 | 2022-09-26 09:45:06 -0700 | [diff] [blame] | 136 | |
| 137 | ### Setup checklist |
| 138 | |
| 139 | <table> |
| 140 | <tr> |
| 141 | <td><strong>Did you setup...</strong></td> |
| 142 | <td><strong>Required setup</strong></td> |
| 143 | </tr> |
| 144 | <tr> |
AndroidX Core Team | e615071 | 2023-08-03 11:06:01 -0700 | [diff] [blame] | 145 | <td>Two modules in <code>settings.gradle</code></td> |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 146 | <td>Both the macrobenchmark and target must be added for your group</td> |
AndroidX Core Team | 6146a74 | 2022-09-26 09:45:06 -0700 | [diff] [blame] | 147 | </tr> |
| 148 | <tr> |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 149 | <td>The module name for the benchmark (<code>com.android.test</code>) module</td> |
| 150 | <td>Must end with <code>macrobenchmark</code></td> |
AndroidX Core Team | 6146a74 | 2022-09-26 09:45:06 -0700 | [diff] [blame] | 151 | </tr> |
| 152 | <tr> |
AndroidX Core Team | 316a0d9 | 2023-08-25 10:53:05 -0700 | [diff] [blame] | 153 | <td>The module name for the app (<code>com.android.app</code>) module</td> |
| 154 | <td>Must end with <code>macrobenchmark-target</code></td> |
AndroidX Core Team | 6146a74 | 2022-09-26 09:45:06 -0700 | [diff] [blame] | 155 | </tr> |
| 156 | <tr> |
| 157 | <td>Name the test class in a discoverable way</td> |
| 158 | <td>Test classes should have standalone names for easy discovery in the |
| 159 | web UI. E.g EmojiStartupTest instead of StartupTest.</td> |
| 160 | </tr> |
| 161 | </table> |