京东小程序团队与华为仓颉团队合影
作者:京东小程序团队
自华为开发者大会(HDC 2024)正式揭晓自研的仓颉编程语言,并面向全球开发者发布HarmonyOS NEXT仓颉语言开发者预览版以来,业界对于仓颉语言实践的关注越来越热烈。仓颉编程语言是一款面向全场景智能的新一代编程语言 ,主打原生智能化、天生全场景、高性能、强安全。
在华为鸿蒙突击队的协助下,京东App小程序框架鸿蒙化版本在高性能场景试点使用了仓颉编程语言,并取得不错的效果,9.9包邮业务页面启动关键场景时长缩短10%,并在10+并发的高负载场景的技术验证中取得了20%+的性能提升。
网络库在移动开发中占据着极其重要的地位,因为现代移动应用几乎无一例外地依赖于网络通信来获取数据和服务。以小程序框架跨端解决方案为例,其在鸿蒙上适配的整体架构图如下:
小程序通常使用H5/JS实现,但其依赖的系统能力(如:系统 API 提供的操作数据库、相机等能力)和基础功能(如:发送网络请求),需要由小程序框架中的鸿蒙端适配层提供。网络库是基础功能的关键组成,其中在鸿蒙系统中网络库选择有系统API,axios三方库、其它三方开源库等。我们在9.9包邮业务的启动响应时延性能分析过程中发现关键耗时为网络请求,因此,我们对网络库的实现进行分析,单个网络请求过程如图:
整个过程分为以下几个阶段:
小程序业务代码发起网络请求: 其中 params 和 callback 来自小程序开发代码(JS)
网络库中前置计算根据用户参数计算出真实网络请求参数
真实网络请求发送在Native侧完成,并维护了一个C++线程池进行网络请求任务的并发管理
得到原始响应结果后进行响应体的后置计算
最终执行业务侧的回调函数
关键问题分析
1. Native线程池开发维护效率低
如下为自定义线程池代码示意
// c++ 并发
// 自定义线程池管理并发任务
class MyThreadPool {
// 线程池初始化
MyThreadPool(size_t poolSize)
// 其它任务维护和执行代码
vector<thread> workers;
vector<function<void>> tasks;
mutex mutex;
}
问题说明:
需要基于C++提供的线程和锁机制,额外开发线程池及并发任务管理代码大约几百行
并发任务粒度是系统线程级别,并发度上限是线程池大小,过大会占用太多资源
并发度:最大只能是n (m >> n时并发能力受限),如图为实际并发任务调度示例,其中m是并发请求数、n是Native侧线程池大小
2. 前置操作、后置操作存在计算耗时逻辑会影响网络请求的整体并发
一、仓颉并发改造
仓颉改造的优化方案如下:
将前置、后置计算中部分耗时的逻辑下沉到仓颉侧,并卸载到仓颉子线程中,充分体现仓颉静态编译高性能的优势
将网络请求改造为仓颉原生并发,充分发挥仓颉的轻量级并发优势
仓颉支持原生并发的特性, 无需维护类似于C++的线程池,任务并发由仓颉轻量级运行时自动管理,轻量化线程,并发性能更好,开销更少。
// 仓颉原生并发示例
func sendRequestImpl(params: Params, callback: (String)=>Unit) {
// spawn关键字直接创建仓颉线程(协程)
spawn {
let realParams = preCompute(params)
let nativeRsp = sendRequest(realParams)
let rsp = postCompute(result)
spawn(Main) {
// 切回主线程
callback(rsp)
}
}
}
仓颉并发度示例如图:仓颉轻量级线程的并发能力和可以和任务数相同,并发度不受限
二、高效互操作体验
仓颉基于元编程能力,提供了ArkTS-Cangjie IDL,提升互操作开发效率和体验
使用eDSL减少冗余代码开发
无需关心传参和返回转换逻辑,大大提升开发效率和体验
// 仓颉侧业务代码开发示例
// 导入仓颉互操作工具包
import ohos.ark_interop.*
import ohos.ark_interop_macro.*
// 声明和ArkTS互操作类型
@Interop[ArkTS]
public class Params {
public var p1: String = ""
public var p2: Int64 = 0
public var p3: Option<String> = None
public var p4: JSHashMapEx<String, String> = JSHashMapEx<String, String>()
public init() {}
}
// 声明和ArkTS互操作函数
@Interop[ArkTS]
public func sendRequest(params: Params, callback: (String)->Unit): Unit {
sendRequestImpl(param, callback)
}
自动生成声明文件
// 自动生成代码
// ark_interop_api.d.ts
export declare class Params {
p1: string
p2: number
p3: string | undefined
p4: Map<string, string>
}
export declare interface CustomLib {
Params: {new (): Params}
sendRequest(params: Params, callback: (funcArg0: string) => void): void
}
// ArkTS 侧使用代码示例
// ArkTS 侧导入必要的库
import { requireCJLib } from "libark_interop_loader.so";
import { CustomLib, Params } from "libark_interop_api.so"
// 加载仓颉二进制库
const cjLib = requireCJLib("libohos_xxx.so") as CustomLib
// 直接使用互操作class类型创建对象
let params = new cjLib.Params()
params.p1 = ""
// ...
let cb = (rsp: string) => {
// 回调逻辑
}
// 直接使用互操作函数
cjLib.sendRequest(params, cb)
本文主要阐述了针对鸿蒙移动开发场景,网络库基础能力的仓颉并发改造的方案,主要应用了仓颉语言的高性能、高并发、高安全等特性,改造基础网络库能力,文中提供了改造方案的示例代码,可供参考。目前该方案已经在京东App小程序场景下9.9包邮业务使能,页面启动关键场景时长缩短10%,并在10+并发的高负载场景的技术验证中取得了20%+的性能提升。实践证明仓颉的静态编译、轻量级并发模型和ArkTS-Cangjie IDL等特性,可以在鸿蒙移动开发中充分发挥其优势,希望本方案为仓颉在其它应用的落地提供有益的参考和借鉴。
点击下方阅读原文,下载仓颉公测版本