Kotlin Multiplatform · On-device Benchmark

Kotlin Multiplatform 三端高频数据 Core:iOS、Android 与 HarmonyOS 的性能实测与工程选型

一套 Kotlin 业务 Core 可以覆盖三端,但高频边界不应强求完全相同。本文用真实设备数据回答:哪里可以直接调用,哪里必须批量化,以及怎样判断内存是否真正稳定。

中文原文发布于 2026-07-21真机 Release无外部图表依赖

Executive summary

结论先行:一份 Core,三种高频边界

“只维护一套源码”约束的是业务事实源,不意味着三端必须共享同一种 ABI、对象表示和界面调度机制。

01 · Android

直接使用 KMP/JVM

共享代码与原生 Kotlin 运行在同一 JVM/ART 体系内,不需要为普通或高频接口增加另一套桥接工具。

02 · iOS

Framework + C ABI 双通道

普通接口继续使用官方 Kotlin/Native Framework;高频批量通道从同一 Core 导出窄 C ABI。

03 · HarmonyOS

CPF-KMP + Direct Buffer

使用 Kotlin/Native 编译共享 Core,以 AKInterop/N-API 同步借用 ArrayBuffer,避免重型对象桥接。

最重要的稳定性判断

KMP 本身不等于内存泄漏。无界输入队列、重复完整快照、跨边界对象图、缓冲区多次拷贝和生命周期未释放,才是高频数据链路中需要被硬性限制的风险。

Methodology

先保证结果一致,再比较性能

三端使用同一场景结构和最终状态校验。快但算错的实现,不进入性能结论。

5,000 行实时数据规模
50 字段单行字段数
34 行可视区域
816 项单批最高变化
20 批/秒常态输入
100 批/秒高压输入

为什么同时看中位数和尾部

中位数表示一半样本快于该值,适合观察典型表现;第 95 百分位表示 95% 的样本不超过该值;第 99 百分位专门暴露偶发抖动。高频链路里,尾部抖动比平均值更容易形成队列积压。

短场景在同一设备上交错运行多个方案,每条车道执行 5 轮。内存同时记录测试前、测试后和最高点,不用单个截图替代完整过程。

为什么还要跑真实时间长稳

把几万次调用压缩到数秒,只能证明吞吐上限。垃圾回收节奏、队列水位、前后台切换和停止后的释放,需要按每秒 20 批、每秒 100 批的真实速率持续运行才能观察。

不同平台的内存统计口径和设备基线不同,因此本文只在同平台、同设备、同负载内比较方案,不用 iOS 数字与 HarmonyOS 数字做平台排名。

Shared-core boundary

共享业务语义,适配平台边界

Core 保持单一事实源;网络、线程、页面生命周期、崩溃采集和 UI 渲染仍属于平台层。

边界原则

跨端契约可以一致,内存表示可以不同。平台 Adapter 只做序列化、调度、生命周期和界面 Patch 消费,不复制一份业务规则。

iOS benchmark

Framework 已可用,C ABI 留给高频批量通道

iPhone 17 Pro Max · iOS 26.5.1(Build 23F81) · Release · 每方案 5 轮

Measured on device

iOS 常态与突发尾部延迟

单批 Core 处理耗时 · 越低越好

方案与指标实测值
Swift 原生 · 常态第 95 百分位0.026 毫秒
Swift 原生 · 突发第 99 百分位0.028 毫秒
官方 KMP Framework · 常态第 95 百分位0.107 毫秒
官方 KMP Framework · 突发第 99 百分位0.115 毫秒
KMP C ABI · 常态第 95 百分位0.094 毫秒
KMP C ABI · 突发第 99 百分位0.103 毫秒
性能分析:官方 Framework 的常态第 95 百分位为 0.107 毫秒,绝对耗时已远低于常见界面刷新预算。C ABI 从同一 Core 导出批量接口后降至 0.094 毫秒;按原始纳秒样本计算改善 12.37%,收益明确但不足以证明所有接口都应改走 C ABI。

Measured on device

iOS Core 理论处理余量

测试进程内每秒完成批次 · 越高越好

方案与指标实测值
Swift 原生 · 常态44,708 批/秒
Swift 原生 · 突发45,731 批/秒
官方 KMP Framework · 常态10,228 批/秒
官方 KMP Framework · 突发10,268 批/秒
KMP C ABI · 常态11,696 批/秒
KMP C ABI · 突发11,699 批/秒
性能分析:官方 Framework 在常态和突发阶段都超过每秒一万批,远高于输入负载。C ABI 约每秒 1.17 万批,适合最热的批量通道;Swift 原生更高,但三条车道都没有吞吐瓶颈。

Measured on device

iOS 最高常驻内存

同一完整场景的进程最高值 · 越低越好

方案与指标实测值
Swift 原生130.47 MiB
官方 KMP Framework148.53 MiB
KMP C ABI136.70 MiB
性能分析:官方 Framework 最高 148.53 MiB,C ABI 为 136.70 MiB,少 11.83 MiB;Swift 原生为 130.47 MiB。C ABI 缩短对象桥接路径后更接近原生,但 Kotlin/Native 运行时底座仍然存在。
iOS 选型

保留官方 Framework 作为默认开发接口,避免为低频调用增加 C API 维护成本;只把高频、批量、可扁平化的输入与 Patch 输出收敛到 C ABI,并在 Swift Adapter 中完成缓冲区管理、latest-only UI Patch 和页面退出释放。

Android benchmark

直接使用 KMP/JVM,不增加第二层桥接

Redmi K20 Pro · Android 11 · Release · 每方案 5 轮

Measured on device

Android 常态与突发尾部延迟

原生 Kotlin 与 KMP/JVM · 越低越好

方案与指标实测值
原生 Kotlin · 常态第 95 百分位0.206 毫秒
原生 Kotlin · 突发第 99 百分位0.239 毫秒
KMP/JVM · 常态第 95 百分位0.118 毫秒
KMP/JVM · 突发第 99 百分位0.176 毫秒
性能分析:本次样本中,KMP/JVM 常态第 95 百分位为 0.118 毫秒,原生 Kotlin 为 0.206 毫秒;突发第 99 百分位分别为 0.176 和 0.239 毫秒。差异只说明本实现通过门槛,不应解读为 KMP 天然快于原生。

Measured on device

Android 最高 PSS

进程实际占用近似值 · 越低越好

方案与指标实测值
原生 Kotlin119.22 MiB
KMP/JVM137.43 MiB
性能分析:KMP/JVM 最高 137.43 MiB,原生 Kotlin 为 119.22 MiB,增加 18.21 MiB、约 15.3%。两条车道均无输入积压,延迟和内存都处于可控范围。
Android 选型

KMP/JVM 与原生 Kotlin 共享 JVM/ART 对象体系。用普通 Kotlin API 直接调用 Core,并在契约层使用批量结构即可;额外引入 C ABI 或 JavaScript 桥接只会增加复制、调试和版本治理成本。

HarmonyOS benchmark

运行时和桥接路径决定内存底座

Mate 60 Pro · HarmonyOS 6.x / OpenHarmony 6.1.0.115 · Release

Measured on device

HarmonyOS 完整流程最高 PSS

同机不同实现的全流程最高值 · 越低越好

方案与指标实测值
ArkTS 原生247.08 MiB
KMP/KNOI 优化前539.60 MiB
KMP/KNOI 优化后405.40 MiB
CPF-KMP + AKInterop83.39 MiB
性能分析:优化后 KMP/KNOI 最高值从 539.60 MiB 降到 405.40 MiB,但常驻底座仍高。CPF-KMP + AKInterop 为 83.39 MiB,比优化后 KNOI 少 322.01 MiB、下降 79.43%,也低于 ArkTS 完整流程的 247.08 MiB。

Measured on device

HarmonyOS 高频边界优化阶梯

从生命周期治理到更换桥接路径 · 越低越好

方案与指标实测值
KMP/KNOI 优化前539.60 MiB
KMP/KNOI 优化后405.40 MiB
CPF-KMP + AKInterop83.39 MiB
性能分析:先治理缓存和生命周期能降低 24.9%,证明对象持有确有优化空间;继续改用 CPF-KMP 与同步 Direct ArrayBuffer 后才跨过绝对内存门槛。收益主要来自低常驻运行时与去重型桥接,不是单纯提高垃圾回收频率。

Measured on device

CPF-KMP 长稳关键内存节点

31.3 分钟、1802 个逐秒样本 · 越低越好

方案与指标实测值
开始采样72.13 MiB
常态输入最高点71.41 MiB
高压输入最高点83.39 MiB
停止输入后恢复45.58 MiB
性能分析:常态实际 19.98 批/秒,高压 99.94 批/秒,最大积压为 0。停止输入后从 76.97 MiB 回落到 45.58 MiB,释放 31.39 MiB;这组曲线不支持“内存随调用次数无界增长”的判断。
83.39 MiBCPF 全程最高 PSS
45.58 MiB停止输入后恢复值
76,820 批完整长稳处理量
62,685,120字段变化总量
99.94 批/秒高压实际速率
0最大积压
HarmonyOS 选型

新方案仍使用 Kotlin/Native,不是 Kotlin/JS。ArkTS 通过 N-API 同步传入可借用的 ArrayBuffer,Kotlin 侧在调用期间读取扁平数据,返回后不保存外部指针。普通低频接口仍可保留声明式调用,高频通道必须保持窄、批量、同步和可测。

Memory governance

七条规则把空间复杂度写进架构

性能优化不能只靠一次合并间隔或一次垃圾回收;队列、对象数量、缓冲区所有权和退出释放都必须有硬边界。

01 · 队列必须有界

消费落后时按键合并、最新值覆盖或执行明确丢弃策略,不能让输入批次无限排队。

02 · 边界传扁平批量数据

跨语言调用传二进制批次、索引和长度,不逐字段创建两套对象图。

03 · 缓冲区复用且不越界持有

调用方预分配,Core 只在同步调用期间借用;返回后不保存外部指针。

04 · 只输出增量 Patch

状态只保留一份,界面只接收变化范围,不在每个批次复制完整表。

05 · 界面侧只保留最新待渲染值

渲染速度低于输入速度时合并刷新,避免 UI 反向撑大 Core 队列。

06 · 生命周期必须显式闭环

取消订阅、切换数据源、进入后台和退出页面都执行明确的停止与释放。

07 · 短测与长测回答不同问题

短测看尾部延迟和吞吐;长测看内存斜率、最高点、恢复值和积压量。

建议的稳定性门禁

首次完整场景记录最高内存;10、30、120 分钟真实速率长稳记录内存斜率和队列深度;停止输入后观察恢复;再覆盖取消、重进、前后台和断线重连。任何阶段都不允许靠无界排队维持“零丢包”。

Decision matrix

最终工程选型

共享的是业务 Core 和契约语义;平台边界根据运行时与高频调用成本选择。

平台默认通道高频批量通道结论
AndroidKMP/JVM 直接 API同一直接 API + 批量结构直接使用共享 Core
iOS官方 Kotlin/Native Framework同一 Core 的 C ABI + Swift AdapterFramework 与 C ABI 双通道
HarmonyOSCPF-KMP 普通接口AKInterop/N-API Direct ArrayBufferCPF-KMP 直接缓冲区通道

业务代码如何保持一套

模型、合并、排序筛选状态、计算规则、增量 Patch 语义和 Golden 校验全部放在 commonMain。三端 Adapter 不得重新实现规则,只负责把平台数据转换为同一批量契约。

出问题如何定位

每个边界记录输入批次、处理批次、丢弃/合并次数、队列深度、Core 对象上限和 UI 待消费 Patch 数;崩溃产物保留各端符号。这样可以先判断问题位于输入、Core、桥接还是 UI,而不是把所有增长都归因于 KMP。

Limits & reproducibility

结论有效,但不是永久免测

设备、系统、编译器、GC 和桥接工具升级都可能改变结果;正式接入仍需真实页面生命周期验证。

  • 本数据证明三个推荐方案在当前负载内达到性能与内存门槛,不证明任意 KMP 代码都自动高效。
  • HarmonyOS 的 31.3 分钟长稳已经回答验证工程内的有界性,正式页面仍应补齐 120 分钟、前后台、重连、取消和退出重进。
  • iOS 的 C ABI 只服务真实高频批量通道;普通接口优先保持 Framework 的类型安全和维护效率。
  • 所有内存值都应结合队列深度、Core 对象数和停止后的恢复值解释,不能只看某一秒的进程数值。

HarmonyOS 复现工具链

项目版本
Kotlin Gradle Plugin2.2.21-0.4.0-08
CPF Kotlin 源码标签v2.2.21-0.4.0
Gradle8.9
AKInterop Gradle Plugin0.0.1
AKInterop KSP2.2.21-2.0.4

相关开源项目与测试版本

这里列出本文涉及的开源项目及本次验证使用的版本,不是三端性能 Demo 的下载入口。项目名称、完整 URL 和操作入口均可打开对应公开仓库。

最终判断

采用 KMP 共享逻辑,不等于把所有平台细节塞进 commonMain。最稳妥的方案是:业务语义只维护一套,热路径按平台选择最低成本边界,内存治理写成可观测、可回归、可拒绝上线的硬规则。