Baseline Profiles and App Performance
Baseline Profiles and App Performance
Baseline Profiles are a form of Profile Guided Optimization (PGO) that improve Android app performance from the first launch by enabling Ahead of Time (AOT) compilation of user facing code paths. Without them, Android Runtime (ART) initially interprets code and gradually optimizes it through Just in Time (JIT) compilation, leaving early users with slower performance. Baseline Profiles shift this optimization to install time, making improvements deterministic and reproducible. By the end of this lesson, you will be able to:
- Explain how Baseline Profiles enable AOT compilation at install time.
- Describe how profiles are generated using the Macrobenchmark library.
- Distinguish between Baseline Profiles and Startup Profiles and their respective optimizations.
- Identify the measurable performance benefits Baseline Profiles provide.
- Apply Baseline Profiles to a release build pipeline.
How Baseline Profiles Work
Without Baseline Profiles, ART uses a tiered compilation approach. On first launch, all code runs in interpreted mode, which is significantly slower than compiled code. As the user interacts with the app, ART identifies frequently executed methods ("hot" methods) and compiles them to native code using JIT compilation. This gradual optimization means the app gets faster over multiple launches, but the first several sessions are noticeably slower.
Baseline Profiles bypass this warm-up period. They are generated during development by simulating user flows such as app startup, screen navigation, and scrolling. The Macrobenchmark library records which methods execute during these flows and compiles them into a .prof file bundled with the APK or AAB.
When the app is installed from Google Play, ART reads the profile and performs AOT compilation on the listed methods. This means the app launches with native compiled code for its most used paths instead of starting in interpreted mode and waiting for JIT compilation to kick in. The compilation happens during installation, not during runtime, so there is no performance cost to the user.
The Compilation Pipeline Without Baseline Profiles
To appreciate what Baseline Profiles provide, it helps to understand the full compilation pipeline. When an app is installed without a profile, ART stores the DEX bytecode as is. On the first launch, ART interprets the bytecode directly. As the user interacts with the app, ART's JIT compiler identifies hot methods and compiles them to native code in memory. This JIT compiled code is lost when the process exits.
In the background, ART periodically writes a profile of observed hot methods to disk. On a subsequent install or after idle maintenance, ART uses this profile for AOT compilation, converting the profiled methods to persistent native code. This means the app reaches full optimization only after several sessions of active use.
Baseline Profiles short-circuit this pipeline by providing the profile at install time. ART performs AOT compilation immediately, and the app launches with native compiled code from the first session.
Generating a Baseline Profile
You define a baseline profile by writing a test that exercises the flows you want optimized:
@OptIn(ExperimentalBaselineProfilesApi::class)
class BaselineProfileGenerator {
@get:Rule
val rule = BaselineProfileRule()
@Test
fun appStartupAndScroll() {
rule.collect(packageName = PACKAGE_NAME) {
startActivityAndWait()
device.findObject(By.text("HOME"))
.clickAndWait(Until.newWindow(), 1_000)
device.findObject(By.res("myRecyclerView"))
.apply {
fling(Direction.DOWN)
fling(Direction.UP)
}
}
}
}
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor