Published August 8, 2026

R8 Makes Kotlin Coroutines on Android Up to 2x Faster with AGP 9.2

The Android engineering team has shipped a significant performance win for every app that relies on Kotlin coroutines. Starting with Android Gradle Plugin 9.2.0, the R8 compiler now optimizes most Atomic*FieldUpdater calls into Unsafe variants that perform two to four times better on common operations. Because the kotlinx.atomicfu library uses these atomics to power kotlinx.coroutines, the practical result is that launching and cancelling coroutines can now run up to twice as fast as before.

The announcement comes from the Android Toolkit and R8 teams, and it touches code that nearly every modern Android app depends on. With the majority of Android apps adopting Kotlin as their primary language, kotlinx.coroutines has become the de-facto standard for asynchronous programming. Jetpack Compose was no exception, adopting coroutines for managing pointer events, animations, and other interactions.

Why Coroutines Were a Hidden Bottleneck

The Compose team began investigating coroutine performance after discovering that they were a bottleneck for many operations happening outside of composition. A striking example emerged: 80% of the time spent on creating and updating Modifier.clickable was consumed by launching and cancelling internal coroutines that handled InteractionSource updates. Much of the early performance work focused on removing coroutines from the default path and delaying initialization until necessary.

To understand the cost of a single coroutine, the team captured Android Runtime method traces. For an empty LaunchedEffect call, the trace separated into three parts: initializing a new coroutine, starting it, and completing it. What stood out was the frequent calls into java.util.concurrent.AtomicReferenceFieldUpdater. While each call is fast on its own, zooming in revealed that most of the time was spent on reflective safety checks.

Coroutines implement a lock-free tree structure for parent-child relationships that makes structured concurrency possible. The kotlinx.atomicfu library implements lock-free atomic operations using AtomicReferenceFieldUpdater, which uses a class reference and a field name to perform atomic operations at runtime. The updater has to run several reflective safety checks to make sure the field exists and is accessible. Every operation in coroutines, whether starting, suspending, cancelling, or completing, calls at least one atomic operation, so a slow updater drags down the whole system.

Measuring the Gap and Fixing It in R8

Before jumping to conclusions, the team benchmarked atomic references from kotlinx.atomicfu against java.util.concurrent.atomic on a Pixel 5 running API 33. The results confirmed the gap: a compareAndSet on a plain AtomicReference took about 50.7 nanoseconds, while the kotlinx.atomicfu version took about 135 nanoseconds, roughly 2.7 times slower. This confirmed that reflective access checks add real overhead at runtime, and that ART does not hide the cost through VM-level optimization.

The insight behind the fix is that most AtomicReferenceFieldUpdater usages are statically obvious, even though the classes also support subtle, dynamic, and reflection-based use. Since the updater initializer is usually static and can be proved to be always correct based on the structure of the surrounding class, the compiler can replace most usages with an internal Unsafe variant during compilation.

  • Instrumentation: introduce offset fields alongside updater fields to enable direct Unsafe access
  • Replacement: replace each call site with a direct Unsafe call when the holder and value types can be statically verified
  • Clean-up: remove unused updater fields, offset fields, and initializing calls that are no longer needed

The optimization is implemented in three parts. Instrumentation introduces offset fields alongside the updater field so the compiler can access the field offset directly. Replacement then optimizes each call site individually, checking that the updater comes from an instrumented field, that the holder is the same class or a subclass, and that the new value matches the declared field type. When all conditions are met, the reflective updater call becomes a direct call to Unsafe. Clean-up then removes unused fields and initializing calls that are no longer needed, with special handling for instrumented fields that are statically known to be free of exceptions.

The results are striking. After these optimizations, kotlinx.atomicfu and most explicit uses of AtomicInt, AtomicLong, and AtomicReferenceFieldUpdater now match plain AtomicReference performance with R8 applied. In some benchmarks they are even faster, because the kotlinx.atomicfu compiler plugin can inline atomic instances into fields, reducing allocations.

What It Means for Compose and Flutter Developers

Jetpack Compose was the main beneficiary of this work. The Compose runtime has microbenchmarks that track coroutine performance closely to catch regressions early. When the benchmarks were updated to a new version of R8, the team noticed a two times improvement when launching and cancelling coroutines in LaunchedEffect. For Flutter developers, the win shows up in any Android native integration that uses Compose or coroutine-backed platform code, making hybrid apps snappier without any source changes.

  • Update AGP: upgrade to Android Gradle Plugin 9.2.0 or above to get the optimization by default
  • No code changes needed: existing coroutine and atomicfu usage is optimized automatically at build time
  • VM-level gains on the way: ART is bringing similar optimizations natively to recent Android versions

Beyond R8, the ART team is implementing these optimizations natively at the VM level. Apps targeting API 37 and running on recent versions of Android may already be optimizing coroutines in a similar way, with benchmarks observing roughly 15% improvement after recent JIT updates.

If you maintain an Android app or a Flutter app with native Android integrations, the upgrade to AGP 9.2.0 is one of the lowest-effort performance wins available this cycle. The build system handles the hard part, and the payoff shows up everywhere coroutines are used, from Compose interactions to network calls to database access.