The Jetpack Microbenchmark library allows you to quickly benchmark your Android native code (Kotlin or Java) from within Android Studio. The library handles warmup, measures your code performance and allocation counts, and outputs benchmarking results to both the Android Studio console and a JSON file with more detail.
We recommend to profile your code before writing a benchmark. This helps you find expensive operations that are worth optimizing. It can also expose why the operations are slow by showing what is happening while they run. These could be running on a low-priority thread, sleeping due to disk access, or unexpectedly calling into an expensive function, like bitmap decoding.
Microbenchmarks are most useful for CPU work that is run many times in your app,
also known as hot code paths. Good examples are
RecyclerView scrolling with
one item shown at a time, data conversions/processing, and other pieces of code
that get used repeatedly.
Other types of code are more difficult to measure with the Microbenchmark library. Because benchmarks run in a loop, any code that isn't run frequently, or performs differently when called multiple times, may not be a good fit for benchmarking.
To learn how to use the library in a continuous integration (CI) environment, refer to Run benchmarks in Continuous Integration.
Avoid measuring cache
Try to avoid measuring just the cache. For example, a custom view's layout benchmark might measure only the performance of the layout cache. To avoid this, you can pass different layout parameters in each loop. In other cases, such as when measuring file system performance, this may be difficult because the OS caches the file system while in a loop.
Code that's run once during application startup is not very likely to get JIT compiled by Android Runtime (ART). Because of that, benchmarking this code with Microbenchmark—where it runs in a tight loop—isn't a realistic way to measure its performance.
To benchmark this sort of code, we recommend using Jetpack Macrobenchmark, which supports measuring higher-level user interactions, like the app launch and scrolling performance.
Obtain consistent benchmarks
Clocks on mobile devices dynamically change from high state (for performance) to low state (to save power, or when the device gets hot). These varying clocks can make your benchmark numbers vary widely, so the library provides ways to deal with this issue.
Lock clocks (requires rooted device)
Locking clocks is the best way to get stable performance. It ensures that clocks never get high enough to heat up the device, or low if a benchmark isn't fully utilizing the CPU. It is applied automatically when running Microbenchmarks with Gradle, or can be applied manually in CI. While this is the best way to ensure stable performance, it isn't supported on most devices, due to requiring rooted Android device.
Sustained performance mode
Window.setSustainedPerformanceMode() is a feature supported by devices that let an app opt for a lower max CPU frequency. When running on supported devices, the Microbenchmark library uses a combination of this API and launching its own activity to both prevent thermal throttling and stabilize results.
This functionality is enabled by default by the
by the Gradle plugin. If you want to use a custom runner, you can subclass the
AndroidBenchmarkRunner and use it as your
The runner launches an opaque, fullscreen activity to ensure that the benchmark runs in the foreground and without any other app drawing.
Automatic execution pausing
If neither clock-locking nor sustained performance are used, the library performs automatic thermal throttling detection. When enabled, the internal benchmark periodically runs to determine when the device temperature has gotten high enough to lower CPU performance. When lowered CPU performance is detected, the library pauses execution to let the device cool down, and retries the current benchmark.
To report issues or submit feature requests when using benchmarking, see the public issue tracker.