AGP 9.2 开始,Android 上协程启动和取消速度提升两倍

从 AGP 9.2 (9.2.0-alpha05)开始,针对 Kotlin 协程内部大量使用的 Atomic*FieldUpdater 场景,R8 会进行优化操作,把一部分原本较重的 Updater 路径,改写成更接近 Unsafe 的低层访问形式,从而做到"启动 / 取消"这类高频路径可以明显加快。

当然,这个处理不是说 Kotlin 本身变快,也不是协程性能完全提升了 2 倍,而是命中了这类原子状态机热点路径时收益带来的提升

在协程内部一直有长期大量依赖 Atomic*FieldUpdater (AFU) ,这在 kotlinx.coroutines 自带的 ProGuard / R8 规则文件也写了,大多 volatile 字段是通过 AFU 更新的,并且连标准库里的 SafeContinuation 也用了 AtomicReferenceFieldUpdater,也就是说 R8 一直可以感知到协程内部这套原子字段更新机制的存在。

而实际上 AFU 在 Android 上确实是一个已知问题 ,2023 年就有人在 kotlinx.coroutines 的 #3950 issue 里提到:

AtomicReference 相比 atomicfu/Atomic*FieldUpdater 后端,在「write 」上大约能快 2 到 4 倍,在 Compose benchmark 的 method trace 里,很多 JobCoroutineContext 操作被 Atomic*FieldUpdater 调用以及对应的类型检查占了大量时间。

实际上 Android 官方 API 文档明确表示过,AtomicReferenceFieldUpdater 是一个 reflection-based utility ,也就是反射式的工具,创建 updater 时还需要 ClassfieldName 去做反射匹配和检查:

官方的文档还特别提到过,它的 compareAndSet 甚至比其他 atomic 类更弱,并且 newUpdater(...) 可能因为字段不可访问、类型不对、不是 volatile 等原因抛异常,换句话说,它的设计目标本来就是"用更通用的方式操作字段",不是"低开销实现"。

而对于协程启动和取消,它本质是一套状态机切换

  • 创建 Job / Continuation
  • 把状态从初始态切到活跃态
  • 注册父子关系
  • 取消时做 CAS(Compare-And-Swap) 更新
  • 解除句柄、恢复 continuation、传播取消

这些步骤里,很多地方都依赖对 volatile 字段做原子读写和 CAS,也就是主要集中在 JobCoroutineContext 等协程基础设施的原子操作上。

如果底层用的是 Atomic*FieldUpdater,每次写入、CAS、lazySet 都要经过一层 updater 语义,而这类 updater 是反射式工具,还带有类型 / 访问相关约束,所以如果 R8 如果能在编译期证明:

  • 这个 updater 绑定的是哪个具体字段
  • 字段访问是安全的
  • 调用点满足替换条件

那么它就可以把「通过 updater 间接操作字段」的逻辑,改写成更底层和直接的实现,类似于把多条机械指令,变成一条简单的机制命令

当然,前面我们说过,这里的 2x 并不是说「 suspend fun 业务逻辑整体都会 2x」,而是当性能主要卡在协程框架本身的创建、状态切换、取消传播这些"原子状态机开销"上时,R8 的这次优化可能让这部分路径接近 2 倍

那为什么是 R8 做,而不是 Kotlin / coroutines 自己做 ?那是因为 kotlinx.coroutinesatomicfu 需要保持:

  • 通用 JVM 兼容性
  • 语义正确性
  • 跨版本可用性
  • 不同运行时 / 不同平台的一致行为

库层面很难直接写死成 Android 私有的底层访问形式, 而 R8 不一样,它在构建 app 时做的是 whole-program optimization,它可以知道:

  • 你最终打包到 Android
  • 具体类、字段、调用点是什么
  • 哪些路径可以安全内联、折叠、改写

Android 官方也明确把 R8 定义为不仅做 shrink/minify,还会做 runtime performance oriented rewriting 的优化器,它会通过内联、逻辑优化、类合并等方式重写代码,提高运行效率。

所以这次优化核心是:库继续保持通用实现,Android 发布链路里的 R8 负责把它处理为为更适合 Android 运行时的版本

也就是,R8 会把 Atomic*FieldUpdater 的抽象调用,改写成与底层 Unsafe 访问语义等价、或者足够接近的实现路径 ,类似于在编译期去抽象层、去反射层、去额外检查层等处理。

所以,这次提升并不是 kotlinx.coroutines 本身的提升,也就不是整个 KMP 的提升,而是 AGP 上 R8 针对 Andorid 平台对 kotlinx.coroutines 的优化带来的提升,R8 在 release 构建时,把协程内部部分 AFU 热点重写成更低层的等价原子访问,从而让协程启动 / 取消这类状态机路径显著加速

所以,你打算上 AGP 9.2 了吗?

相关推荐
Ulyanov2 小时前
Python与YAML的优雅交响:从配置管理到数据艺术的完美实践 (一)
开发语言·前端·python·数据可视化
SuperEugene2 小时前
Python 函数与模块化:前端工程化思维完全通用| 基础篇
前端·python·状态模式
萧行之3 小时前
Ubuntu Node.js 版本管理工具 n 完整安装与使用教程
linux·前端
devlei9 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
Jagger_10 小时前
周末和AI肝了两天,终于知道:为什么要把AI当做实习生
前端
weixin_4561648310 小时前
vue3 子组件向父组件传参
前端·vue.js
沉鱼.4410 小时前
第十二届题目
java·前端·算法
Setsuna_F_Seiei11 小时前
CocosCreator 游戏开发 - 多维度状态机架构设计与实现
前端·cocos creator·游戏开发
Bigger11 小时前
CodeWalkers:让 AI 助手化身桌面宠物,陪你敲代码的赛博伙伴!
前端·app·ai编程