共计 5327 个字符,预计需要花费 14 分钟才能阅读完成。
作者:xianyuqiang 编译器首席架构师
ArkCompiler(方舟编译器)是组件化、可配置的多语言编译和运行平台,它既能撑持繁多语言运行环境,也能撑持多种语言组合的运行环境。它目前次要反对的语言是 JavaScript、TypeScript 和 Java。
一、概述
HarmonyOS 的设计指标,是成为买通手机、PC、平板、电视、车机和智能穿戴等多种设施的对立操作系统。
图 1 多设施互联
其利用开发有多编程语言、多范式的反对需要,其中高级编程语言包含 JavaScript、TypeScript、Java 等,开发范式包含申明式 UI 范式、分布式编程范式。咱们须要相应的编译器和运行时来撑持这些高级利用编程语言的高效开发、部署和运行。使利用开发者能应用同一套开发框架实现一次开发多端部署运行。并且让应用 HarmonyOS 设施的用户,能取得对立的用户体验。于是,ArkCompiler 应运而生。
- 指标
ArkCompiler 是为反对多种编程语言、多种芯片平台的联结编译、运行而设计的对立编程平台,其设计指标是提供一个语言可插拔、组件可配置的多语言编译器运行时。
语言可插拔:设计架构上反对多种语言接入,ArkCompiler 有能力提供具备高效执行性能且具备跨语言劣势的多语言运行时,也能够在小设施上提供高效轻量的繁多语言运行时。
组件可配置:ArkCompiler 具备丰盛的编译器运行时组件零碎。通过定制化配置编译运行时的语言和组件,以反对手机、PC、平板、电视、汽车和智能穿戴等多种设施上不同的性能和内存需要。
- 架构
如图 2 所示,ArkCompiler 蕴含编译器、工具链、运行时等关键部件。ArkCompiler 工具链实现对应语言的前端编译器,将前端开发框架的高级语言编译成对立的字节码 / 二进制文件。依据不同的利用场景,通过 ArkCompiler 运行时解释器解释执行字节码文件或 JIT/AOT 编译器编译执行对应体系架构的优化机器码,从而晋升运行效率和启动性能。
图 2 ArkCompiler 运行原理
上面,本文将从前端编译器,运行时开展介绍。
二、前端编译器
前端编译器是高级语言通往语言运行时的桥梁,它依照语言标准,将编程语言表白的语义翻译为运行时可能了解的介质,在 ArkCompiler 解决方案里,这体现为 ArkCompiler 字节码。即图 3 中的 ArkCompiler Bytecode(简称 abc)。局部语言,也反对通过 ArkCompiler 的 AOT Compiler 组件间接将字节码编译成对应体系架构的优化机器码。
图 3 ArkCompiler 前端
- 前端编译器性能
在须要反对多种语言的 ArkCompiler 中,前端编译器的次要作用是在 Host 侧把源码生成字节码文件,这样的长处:
利用 Host 弱小的计算能力,可能在运行前做更多更简单的算法优化,缩小运行时的工作,进步运行效率。
相比常见的 JavaScript 运行时,能够把端侧的编译解析过程提前到公布前,晋升程序的启动性能。
图 4 JavaScript 运行流程
编译优化
ArkCompiler 提供对 TypeScript(TS)的原生反对。在前端编译 TS 源码时,会利用 TS 的显式类型申明,利用类型推导进行类型优化,并且将推导出的类型信息通过字节码文件保留至运行时,由此运行时能够间接利用类型信息执行疾速门路。此外,动态的类型剖析和推导也使得 TS AOT (Ahead of Time) Compiler 成为可能,动态剖析失去的类型信息帮忙 AOT Compiler 间接编译生成高质量的机器码,使得 TS 源码能够间接以机器码模式运行,进一步晋升运行性能。
图 5 编译优化
- ArkCompiler 字节码
ArkCompiler 字节码(ArkCompiler Bytecode)是运行时解释器可能解析运行的一种硬件和平台无关的两头表现形式,以紧凑、可扩大、多语言反对作为设计指标。屏蔽设施的差别,反对利用的跨设施散发、部署和运行。ArkCompiler 采纳的是基于寄存器的字节码格局。每个寄存器的宽度为 64 位,最多反对 65536 个寄存器。
(1)寄存器
ArkCompiler 寄存器要求可能搁置对象援用和根本类型,宽度采纳 64 位。寄存器的作用域是以函数栈帧为范畴。在字节码指令编码中,寄存器索引反对 4 位、8 位以及 16 位的变长编码,在反对办法内不同数量范畴的寄存器寻址的同时减小字节码尺寸。
(2)累加寄存器
累加寄存器,俗称累加器,是一个非凡的寄存器,被指令隐含应用。应用累加器的次要目标是在不损失性能的前提下改善指令编码密度。在 ArkCompiler 字节码中,上一条指令利用累加器作为后果输入,下一条指令将此累加器作为输出,能够无效改善指令密度,减小字节码的尺寸。同时,通过在生成字节码阶段的数据流及控制流剖析和优化,前端编译器能够无效打消冗余的累加器 load 和 store 操作。
(3)根本类型反对
ArkCompiler 字节码提供对 32 位(i32)和 64 位(i64)整型数值的寄存器操作反对,8 位和 16 位数值通过扩大到 32 位来模仿。反对对 IEEE-754 双精度浮点 f64 值的寄存器的操作,f32 数据类型(IEEE-754 单精度)也通过转换为 f64 值进行模仿。根本数据类型不须要虚拟机进行记录、跟踪和推导,而是通过操作不同根本数据类型的专用字节码进行示意,包含整数值的符号性。为了更无效地利用字节码的指令空间,设计中对高频应用的数据类型和操作引入更多的专用字节码,而对低频应用的数据类型和操作采纳更通用的字节码。
(4)语言相干类型反对
ArkCompiler 依据其执行的语言反对层次化的类型零碎。这样,创立或者从常量池加载的字符串、数组、异样对象等,都是含有相应档次关系的、和具体语言标准相匹配的数据对象。
(5)动静类型语言反对
为反对相似 JS/TS 的动静类型语言,ArkCompiler 通过非凡的标记值(”Any”)示意动静类型值,其包装了值自身和相应的类型信息(包含根本类型和对象援用类型数据)。虚构寄存器的宽度能够包容“Any”值。同时,在动静类型语言代码的执行上下文中,也可能应用到蕴含类型查看指令在内的动态确定类型指令序列,以示意动静类型相干语义。
三、ArkCompiler 运行时
ArkCompiler 运行时,如图 6 所示,被分为了外围运行时(Core Runtime)和各自语言独立的运行时插件(Runtime Plugin)。
外围运行时次要由运行时的公共外围组件形成,蕴含定义字节码格局和行为的 Public ISA 模块,对接零碎调用的 ArkCompiler Base Platform 模块,反对 Debugger、Profiler 等工具的 Common Tool 模块和承载字节码文件解决的 ArkCompiler File 模块等。也提供了可选的语言无关的解释器、内存治理、编译器和并发等基础设施组件。
各语言运行时插件则蕴含各语言特有的个性实现以及规范库来撑持语言的运行行为合乎对应的语言标准,由各语言按需定制。
图 6 运行时框架
- 执行引擎
ArkCompiler 运行时执行引擎有多种组件,包含解释器、JIT 编译器和 AOT 编译器,如图 7 所示。
图 7 执行引擎构造
(1)解释器
解释器可间接运行前端编译器输入的字节码。
(2)JIT Compiler
JIT 编译器个别须要运行时执行代码一段时间,Profiler 生成了 profiling 数据之后,依据 profiling 数据即时编译生成高质量的机器码(上图 Optimized Code II)来运行。(JIT 能够依据代码执行状况实时编译生成最优机器指令)
(3)AOT Compiler
AOT 编译器则是在运行前依据动态信息间接编译生成高质量的指标机器码(上图 Optimized Code I)在设施上运行,PGO(Profile Guided Optimization)配置文件能够作为 AOT Compiler 的输出之一,给 AOT Compiler 一些批示,比方编译的范畴以及编译某个办法时应用哪些优化技术。通常这种 PGO 配置文件由在等同规格的设施上通过运行时 profiling 或者大数据分析生成。
无论是 JIT 编译器生成的优化代码,还是 AOT 编译器生成的优化代码,通常都是在肯定优化假如或者优化推断的前提下生成的。如果这个前提在运行时不成立,则须要进行 Deopt(逆优化),回退到解释器执行,这种状况个别较少产生。
- 定制化需要
各个执行引擎的性能如图 8 所示:
图 8 各执行引擎的性能比照
ArkCompiler 运行时通过不同执行模式的按需组合,反对多种设施不同的定制化需要。
在低端 IOT 设施上,ArkCompiler 执行引擎反对纯解释器的执行模式,以满足小设施的内存限度条件;
在高端设施上,ArkCompiler 执行引擎反对解释器配合 AOT 编译器以及 JIT 编译器的模式运行,对相当局部代码应用 AOT 编译器编译,使得程序一开始就能够运行在高质量的优化代码上,取得最好的执行性能;
在其它设施上,则依据设施的硬件条件限度来抉择策略,设定高频应用须要 AOT 编译的代码范畴,其它代码则依附解释器配合 JIT Compiler 运行,使得利用执行性能可能失去最大化。
为了晋升解释执行性能,在特定的体系架构下,解释器约定了将解释执行上下文中某些频繁应用的数据放在对应的物理寄存器中,比方在 Arm64 架构下,上下文中以后字节码指令地址、累加器值、解释器栈帧、指令映射表、以后线程对象等,间接放在固定的寄存器上,防止了在栈上频繁的加载和写入操作。
- 并发
简单挪动利用的开发和运行对并发有较强的需要。ArkCompiler 运行时除了提供规范的“Java 多线程编程”和“运行反对”之外,也提供响应式的 Actor 并发编程模型反对。此模型下执行体之间不共享任何数据,通过音讯机制进行通信。以后,业界的一些 Actor 并发模型,例如传统 JS 引擎的 web-worker 实现,有启动速度慢、内存占用低等缺点。
为了利用设备的多核能力取得更好的性能晋升,在 Actor 内存隔离模型的根底上,ArkCompiler 运行时通过共享 Actor 实例中的不可变或者不易变的对象、内建代码块、办法字节码等,晋升 Actor 的启动性能和节俭内存开销,达到实现轻量级 Actor 并发模型的指标。
图 9 轻量级 Actor 实现
- 跨语言优化
HarmonyOS 利用在某些状况下实际上是由多种语言的代码组成的。例如对 HarmonyOS JS/TS 利用,有一些零碎库、框架和利用依赖的局部能力的实现应用了 C /C++ 和 Java 语言。HarmonyOS 开发框架也提供了 JS/TS 与 C /C++ 交互的 JS NAPI 以及 JS/TS 与 Java 交互的 Channel 机制。思考不同语言之间的交互场景的开发和运行效率需要,ArkCompiler 和开发框架联结设计,提供了对应的优化机制。
(1)JS/TS 与 C /C++ 交互
在 TS 版本的操作系统平台 API 实现中,通常须要面临 C /C++ 代码拜访和操作 TS 对象的场景。对这个业务场景,ArkCompiler 能够依据 TS 源码的 class 申明和运行时约定,生成蕴含 TS 对象布局形容的 C /C++ 头文件,以及操作这些 TS 对象的 C /C++ 实现库。在 C /C++ 代码中,通过蕴含 TS 对象形容头文件以及链接对应实现库,实现间接操作 TS 对象的成果。须要阐明的是,因为 TS 类型或其内在布局并非总是固定不变的,因而在 TS 对象操作的代码实现中,会插入类型查看,如果对象类型或布局在运行时发生变化,则回退执行通用的慢速门路。
图 10 跨语言交互
(2)JS/TS 与 Java 交互
HarmonyOS 中有一些利用所需的能力是通过零碎、框架或利用的 Java 库提供的。因而在 HarmonyOS 利用中,也存在较多 JS/TS 代码与 Java 代码交互的场景。常见的案例中,因为 JS/TS 代码和 Java 代码有各自独立的运行环境,相互之间对于对方的数据表示、调用约定都是不可知的,所以 JS/TS 与 Java 的数据交互通常须要通过规范的 JSON 序列化和反序列化流程,以及经由 Native 层桥接的互相调用。这造成在一些场景中开销较大,影响用户体验。
ArkCompiler 利用同时反对多语言的劣势,运行时具备不同语言的数据表示、对象布局、函数调用约定等信息,这使得跨语言之间的间接数据拜访、对象操作和办法调用成为可能,同时 Java 代码提供的更多确定的类型信息也成为 JS/TS 类型推导的额定输出,利于对 JS/TS 的编译优化。另一方面,这也使咱们能为开发者提供一个更简化的多语言编程模型,缩小须要额定手工编写的业务无关的跨语言交互代码工作量。
图 11 简化的多语言编程模型
四、总结
HarmonyOS 所反对的 IoT 时代下,联合利用生态、开发体验和用户体验等方面的需要,ArkCompiler 与硬件、操作系统、开发框架、编程语言协同设计,在多语言对立编译运行和多设施反对的根底上,实现对 HarmonyOS 利用在开发和运行效率等方面的晋升。
将来,ArkCompiler 在继续优化根底体验的同时,更会进一步联合 HarmonyOS 万物互联的需要,在跨端迁徙、多端协同等翻新场景,从编译器和运行时等方面提供底层的解决方案和优化机制,晋升分布式应用的开发和运行体验。