Kotlin跨平台语言Benchmark与Interop能力深度分析

Viewed 0

跨平台语言Benchmark大横评

在这个部分,我们将对几种主要的跨平台语言进行比较,主要从执行效率、引入测试用例前后应用体积变化、运行内存峰值和运行内存开销这几个方面进行考察。对比测试在iOS、AndroidOS和HarmonyOS这三个平台上进行,由于不同平台的硬件设备无法做到完全一致,我们在各平台选择一款设备作为测试标准。对比的语言包括目前在实际生产环境中使用到的Kotlin、JavaScript、Dart、C++和Swift。

测试方式

测试集
考虑到真实研发场景中多数操作集中于Model处理,并且积累了较多的真实业务场景的Protobuf文件,我们使用这些Proto文件作为测试集,并以最常见的序列化与反序列化作为测试用例,在各种语言上进行测试。本文选取了一组常用的Protobuf文件,共237个,总体积为2.2MB。

测试流程

  1. 基于每个平台和每种测试语言的环境构造测试工程,包括基本的UI调用和统计能力,确保测试用例依赖不被剥离。
  2. 构造测试用例:使用Protoc插件生成对应语言的代码,并自研了各语言的测试插件,生成每个Proto文件对应的序列化反序列化测试代码。测试代码会遍历所有Message,构造对象后进行序列化和反序列化操作。例如,在C++、Kotlin和Swift中分别生成了相应的测试模块代码。
  3. 编译、链接和运行:在不同平台上使用默认的Release编译方式,对于Dart则使用Flutter的AOT模式进行测试。在iOS中使用JavaScriptCore,在Harmony中使用默认的ArkRuntime作为JavaScript运行时。
  4. 通过各平台的Profile工具人工收集数据。

测试结果
在性能测试中,我们取运行50次测试用例时间的平均值。Kotlin在iOS平台上表现出色,执行效率与原生Swift处于同一数量级,部分项甚至更优。在体积方面,C++通过优化仍有进一步的Strip空间,但测试中采用了各工具链的默认Release构建行为。在内存方面,JavaScript由于执行效率相对较低且内存占用较大、垃圾回收不及时,导致峰值内存较高;而相对于垃圾回收语言,所有手动内存管理语言的内存波动都较小。

为什么语言双向调用Interop能力至关重要

在跨平台开发中,我们经常区分跨平台语言和跨平台UI框架。成熟的跨平台UI框架往往追求在框架内自闭环,而跨平台语言的引入更多是为了解决工程化问题,如代码复用和开发效率。这意味着跨平台语言需要能够调用平台能力,同时平台也需要能够调用跨平台语言的能力。在一个健康的工程结构中,越底层的代码越稳定,互调用的变更频率越低;而上层的业务模块中,双向Interop能力尤为重要。

我们对主流跨平台语言的Interop能力进行了主观评价,评价标准包括接近原生体验、需要大量样板代码或基本可用。评价涉及的语言包括C++、Rust、Kotlin、JavaScript和Dart。

没有独立运行时的语言
对于没有独立运行时的语言,如C++、Rust和Kotlin,互操作性依赖于编译器和链接器的能力,以及平台操作系统提供的接口绑定。

  • C++:在iOS平台上,可以通过extern "C"调用C代码,或通过Objective-C++调用C++代码;在Android上通过JNI反射调用Java代码;在Harmony平台上通过NAPI注册和调用。
  • Rust:相对于C++,需要额外工具生成绑定代码,无法直接与Swift交互,但生态中有工具简化此过程。
  • Kotlin:通过Cinterop生成Objective-C和Swift的绑定代码,在iOS上无法直接与Swift交互,但可直接导出ObjC头文件供iOS调用;在Android上可直接与Java/Kotlin代码互调;在Harmony上基于JavaScript体系,对API进行绑定描述。

有独立运行时的语言
对于有独立运行时的语言,如JavaScript和Dart,互操作性依赖于运行时能力。

  • JavaScript:通过各运行时定义的Bridge能力注册平台功能,在iOS和Android上可通过运行时调用本地代码,在Harmony上由于基于ArkRuntime,基本实现完美互调。
  • Dart:通过工具生成对Java和Objective-C的绑定代码,但在独立运行时方面不够成熟,主要依赖Flutter的Channel机制进行互调。

跨平台开发是不是伪命题?

在大前端领域,产品和研发分工明确。随着移动设备多样化,研发团队面临多平台开发,但高复用度可以降低运营成本并提升迭代速度。产品需要在不同终端保持核心体验的一致性,同时允许平台差异化。目前主流做法是在底层模块使用C++等技术实现跨平台,而在顶层业务通过原生技术实现。

团队沟通成本与技术复用成本
高复用度往往伴随更高的协作复杂度。在快速变化的互联网环境中,管理者可能倾向于低复用度以快速应变。但根本原因在于轻易拒绝变更和缺乏技术沉淀。经济下行周期使得团队结构更紧凑,降低了沟通成本,凸显了技术复用的优势。跨平台技术带来的效益包括成本降低、研发效率提升、多端一致性和风险管理。

AI与跨平台
大语言模型的发展带来了自动化工具,提升技术复用效率。但在多平台异化下,自然语言到程序语言的翻译过程更复杂,积累专家语料更困难,可能影响AI辅助效率。随着鸿蒙操作系统的出现,研发团队面临三个平台的挑战,跨平台技术有助于降低沟通成本。

为什么B站选择了Kotlin作为跨平台语言的基石

跨什么层?
在移动开发中,共识是将核心模块如播放内核、网络内核等通过C++或Rust实现跨平台,而业务领域通过原生技术实现。但业务领域的复杂度更高,代码规模和逻辑分支远超核心模块。因此,B站选择为上层业务领域实现跨平台,贯彻View与Data分离的思想,让平台差异化由自身实现。

Why Kotlin?

  • 双向Interop能力:作为必选项,Kotlin支持与各平台原生交互。
  • 高阶语法特性:现代语言特性如异步能力通过工具链消化,减少样板代码。
  • 性能表现:在Benchmark中,Kotlin在iOS平台与Swift性能相当,在Android平台为原生支持,在Harmony平台虽有劣势但可接受。
  • 社区生态:由JetBrains开发,有丰富的第三方库和工具支持。

地利人和
B站自2017年起采用Kotlin-first策略,积累了丰富的Kotlin使用经验和工具库。Kotlin Multiplatform现已稳定,解决了历史上的并发等问题。在项目调研中,发现由其他平台同学操刀落地更有优势,促进了团队协作。

典型案例场景举例

各页面的UIStore
需求:在推行的单向数据流模型中,抽象Action和State,实现Reducer转换。问题包括多端代码重复、沟通成本高和UI与逻辑耦合。方案是引入FlowRedux状态机,规范化模块接口,实现统一的Store。收益包括促进各平台向数据驱动演进、减少代码规模;痛点涉及构建系统优化、ObjC无默认参数以及异步接口包装。

直播消息推送中间件
需求:核心消息推送引擎负责长链接消息处理。问题包括业务间重复实现、终端差异化和消息不一致性。方案是接口化核心能力,实现统一的消息处理中间件,使用Protobuf和Codegen简化消息序列化。收益包括减少模板代码和统一收口;痛点涉及增量编译性能问题。

跨UI与动态化(未来)

跨平台UI
跨平台UI是前端领域的终极体现,但平台差异性是商业价值所在。基于KMP的Compose Multiplatform允许低试错成本尝试跨平台UI,适用于通用快速产品形态。

动态化
动态化主要目的应是降低软件复杂度,而非仅限低代码或热更新。通过高度组件化和DSL化,结合声明式框架,可以借鉴现有实践如Redwood Treehouse进行未来探索。

跨平台自身的成本问题

代码仓库策略

  • One Repo:理想情况,但需融合现有沉淀。
  • Two Repo:以Android工程为主改造为Multiplatform,但存在代码迁移痛点、工具链复杂度和团队割裂问题。
  • Three Repo:采用Android、iOS、Harmony和KMP独立仓库,优势在于各团队专注自有工具链,劣势是工具链复杂度增加和代码管理困难。B站实践了四种研发角色分工,并致力于最终回归One Repo。

人性因素
通过收集种子研发、透明化管理、强文档化和直接参与需求实现,降低团队抵触。技术选型上使用Bazel构建系统扫清障碍,长远目标是通过KMP提效后整合为One Repo。

总结

本文客观比较了移动领域多个跨平台语言的性能,并主观探讨了跨平台技术的思考和B站的选型。Kotlin凭借其Interop能力、性能表现和生态优势,成为B站跨平台业务的基石。未来将持续输出KMP技术实践,反哺社区建设。

0 Answers