国产编程语言MoonBit发布原生后端,比Java快15倍,拥抱 RISC-V

图片
我们从去年八月份看到知乎上的一个热门问题:「为什么中国出了这么多厉害的互联网公司,但没有自己设计过编程语言?【1】,在问题回答中出现了 MoonBit 编程语言的身影,随后我们开始追踪报道它,目睹 MoonBit 以惊人速度成长蝶变,深感中国基础软件潜力无穷。

MoonBit 诞生于 2022 年,是 专为云及边缘计算设计的 AI 云原生编程语言及其完整的工具链, 语言设计上吸收了 Rust 和 GO 的优秀设计理念 MoonBit 在 2023 年 8 月 18 日首次对外展示时 ,就为WebAssembly 这一跨平台的标准指令集提供了原生支【2】,运行速度和代码体积方面领先于传统编程语言。值得注意的是: 「MoonBit 一开始就有非常好的 IDE 支持,其在线 IDE【3】是目前工业级编程语言里唯一一个允许离线运行的集成开发环境,响应速度和本地一致。

随后 MoonBit 又逐步提供原生的测试工具(Debugger)和 AI 代码生成助手。MoonBit 于今年 5 月 宣布支持 JS 后端,而且性能方面成绩亮眼,在 JSON5 的库上甚至比 原生 JS 有近 8 倍性能的优势【4】

MoonBit 于 9 月进入到 Beta 预览版【5】,比大部分主流语言更早推出现代化泛型、精准错误处理和高效迭代器等重要特性。

现在 MoonBit 给我们带来了一个更大的惊喜,在原有 WebAssembly 和 JS 后端的基础上,新增了原生后端的支持,未来将不再借助于虚拟机,可以原生编译到机器码,在硬件上运行。MoonBit 这一原生后端也支持 RISC-V 开源指令集,这使得 MoonBit 可以直接与硬件交互,将来可以在嵌入式、IoT 以及系统编程等更多场景和领域施展拳脚。

为什么要支持 Native 原生后端

编程语言按照运行时实现方式主要分为两类:一类是基于虚拟机的,比如 Java,Kotlin 以及绝大多数脚本语言;另一类是不基于虚拟机直接在硬件上执行的,如 C/C++/Rust 和 Go 语言。一般来说编程语言的性能以及开发体验很大程度上取决于编译器实现的质量,而原生后端具备更大的优化空间,其能达到的性能天花板也会更高,当然相对来说难度与挑战也显著增加。

开发一门原生编程语言绝非易事 -- 例如新兴的系统编程 语言 Zig【6】 经历十年左右的研发,目前仍处于 Beta 阶段,可见原生后端的开发难度不可谓不高,但其带来的长期利益和竞争优势能让一门编程语言收获值得的回报。

MoonBit 已经支持的 WebAssembly 和 JS 后端是依赖于虚拟机的,虽然这两者都拥有广泛的生态,但是在一些对性能要求十分苛刻的场景仍然力不从心。通过对原生后端的支持,MoonBit 编程语言现在可以满足绝大部分的应用场景。

MoonBit 的原生后端能将 MoonBit 编译成目标机器的可执行二进制,从而使得 MoonBit 程序能直接运行在目标机器上,无需任何虚拟机或运行时。这意味着 MoonBit 的原生后端没有任何解释开销,可以充分利用硬件资源,如 CPU、内存和存储,从而在性能上有显著提升。并且对于计算密集型或数据密集型的应用,如视频处理、机器学习或大规模数据分析,原生后端可以提供必要的性能。

接下来,我们通过实际的性能数据展示 MoonBit 原生后端在不同应用场景下的表现:

1. 数值计算领域 MoonBit 
相比 Java 快一个数量级

原生语言相比于基于虚拟机的编程语言的性能优势在数值计算领域尤为明显,这得益于原生编程语言能更好的优化内存布局和利用硬件加速指令。

在这个经典的 FFT 算法 benchmark 中【7】,同样的 Cooley-Tukey 算法, MoonBit 相比 Java 有 15 倍以上的性能提升,比最先进的商业版本的 Java 编译器 GraalVM 也有 4 倍以上的性能提升。

下面是 MoonBit 与 Java 实现的 FFT 算法的性能对比。FFT 是一个计算序列的离散傅里叶变换的高效算法,在信号处理、压缩等许多领域有重要应用。FFT 算法涉及大量数值运算和数组操作,可以很好地体现语言的基础性能:

图片

与使用 C/C++ 等底层语言不同,MoonBit 在带来巨大性能提升的同时,没有牺牲任何开发体验。MoonBit 与 Java 同样是自动管理内存的语言,用户无需被手动管理内存的负担困扰。此外,MoonBit 在语言层面具有更现代的设计,提供了比 Java 更多的现代语言特性来提升用户的开发体验。

对于数值计算类程序,性能主要取决于两方面:对计算本身的优化、以及语言本身额外开销的大小。C/C++ 在数值计算领域的优异性能,除了因为编译器的各种优化,语言本身几乎没有额外开销也是一个重要原因。对于自动管理内存的高级语言来说,消除各种高级语言功能带来的开销是提升性能的关键。此外,由于现在 CPU 的缓存机制,数据的布局也对性能有很大影响。MoonBit 能做到运行性能与开发体验的双重优越,源自于科学、现代的顶层设计:

  • MoonBit 在语言设计上追求简洁,语言自身带来的额外性能开销非常小,数据结构的布局更紧凑,能更好利用现代 CPU 的缓存架构

  • MoonBit 采用多层 IR、全局优化的编译架构,能够有效消除泛形等高级语言功能的开销,对数据布局等进行全局的优化

2. 内存管理性能优于 Java & Swift

数值计算类任务能很好反映编程语言的基础性能。但在应用类程序中,更常见的是对内存中数据结构的各种操作。得益于定制的内存管理系统,MoonBit 的原生后端在内存操作性能与内存占用上表现也非常优秀,优于拥有高性能 GC 的 Java 与同样编译到二进制、自动管理内存的语言 Swift:

图片

上述 benchmark 来自论文:「Perceus: Garbage Free Reference Counting with Reuse【8】,通过对语言在大量动态内存分配操作时的运行时间内存占用情况进行比较,从而很好反映语言的内存管理系统的性能。

编程语言的自动内存管理系统一般可分为 tracing GC 和引用计数两大类。Tracing GC 往往具有更好的性能,但内存占用较大;而引用计数虽然性能不如 tracing GC,但具有内存占用小、实时性好的优点。得益于 MoonBit 的多层优化和定制的内存管理系统,MoonBit 能够做到在大部分场景性能优于或接近使用高性能 tracing GC 的 Java,且远优于使用引用计数的 Swift。同时,在内存使用效率上,MoonBit 的内存占用略优于使用引用计数的 Swift,在一些 benchmark 上相比 Java 能节省数倍的内存。

在嵌入式与 IoT 设备上,计算资源与内存资源都非常紧张。因此编程语言的运行性能与内存占用表现都必须足够好,才能胜任嵌入式 /IoT 开发的场景。实践中,受制于硬件资源,嵌入式 /IoT 开发者往往不得不使用 C/C++ 等底层语言来获得足够的性能。但这会极大增加开发者的心智负担、降低开发效率。而 MoonBit 原生后端提供了一种新的可能性:使用 MoonBit 原生后端开发程序,能够同时在开发体验、运行性能与内存占用三点上兼具优势。

从以上两个场景可以发现,虽然 MoonBit 原生后端还处于早期阶段,但在性能上蕴藏着巨大潜力,未来众多硬件的性能将通过 MoonBit 充分释放。

  支持不同指令集架构,
RISC-V 一等公民

MoonBit 原生后端采用多层 IR 设计,其中最后一层 IR 是 C 语言的一个子集。这使得 MoonBit 可以和 C 语言有非常好的互操作,没有昂贵的 FFI 开销,充分利用已有的各种高性能 C 语言库。未来 MoonBit 将提供规范化的 C 语言调用约定,可以集成已有的开源生态的 AI 推理库,用于高性能计算和 AI 边缘推理领域,或用于编写直接调用操作系统 API 的高性能应用与服务。以 C/C++ 语言为主的原生生态是整个软件生态中非常重要的一环。操作系统的底层 API、各类高性能计算库、AI tensor library 如 llama.cpp、ggml 等,都是通过 C ABI 提供服务的。MoonBit 的原生后端能够无缝与基于 C ABI 的第三方库交互,为 MoonBit 解锁新的开发场景,并带来极佳的用户体验。

MoonBit 原生后端能够生成独立运行的高效的可执行二进制。因此,MoonBit 的原生后端非常适合用于编写嵌入式、IoT 程序。使用 MoonBit 编写这些程序,在具有优异性能的同时,还能享受到 MoonBit 优秀的开发体验与各种现代语言功能。此外,由于 MoonBit 生成的二进制是静态链接无依赖的,可以更加便捷的将 MoonBit native 程序部署到各类边缘设备上。

原生后端的另一个优势就是深度硬件集成,原生应用能够直接访问设备的硬件功能,如摄像头、GPS、传感器等,这对于需要这些功能的后端服务至关重要。并且深度硬件集成可以提供更快速的响应时间和更丰富的功能,增强用户体验。除了生成开发机器的原生二进制,MoonBit 的原生后端还能交叉编译到所有 C 语言支持的平台,在第一时间实现了对 RISC-V 平台的原生支持,拥抱嵌入式与 IoT 生态。

RISC-V 是一个基于精简指令集计算(RISC)原则的开放指令集架构(ISA),允许任何人设计、制造和销售 RISC-V 芯片和软件。RISC-V 架构以其完全开源、架构简单、模块化设计和完整的工具链等特点而受到青睐,目前已在全球范围内得到广泛的应用和部署。通过支持 RISC-V ,MoonBit 可以更好地发挥在物联网(IoT)、人工智能(AI)、边缘计算、移动设备、服务器和开发者社区与教育等领域的潜力。

下面是一个使用 MoonBit 交叉编译到 RISC-V 的例子。为了获得开箱即用的交叉编译体验,首先需要安装 Zig。接下来,我们创建一个新的项目:

moon new hello-riscvcd hello-riscv
{  "is-main": true,  "import": [    "username/hello/lib"  ],  "link": {    "native": {      "flags": [ "-cc", "zig cc -target riscv64-linux-musl -O2 -static" ]    }  }}

接下来,只需要执行:

moon build --target native

就能获得 target/native/release/build/main/main.exe,一个 RISC-V 二进制可执行文件。可以用 RISC-V 虚拟机或物理机直接运行这个可执行文件。这里以 Linux 下 libriscv【9】提供的虚拟机为例:

$ rvlinux ./target/native/debug/build/main/main.exeHello, World!
1. 极致性能的体验下
依然保持内存安全

相比于 C/C++ 等系统编程语言,MoonBit 在保持高性能的同时依然注重内存安全和可靠性。MoonBit 保障内存安全的方式与 Rust 不同。Rust 通过去除 GC 来支持系统编程,但这样给开发者带来了陡峭的学习曲线。MoonBit 希望在不影响开发者体验的情况下,在大部分开发场景依赖编译器自身的优化去除 GC 所带来的开销。在一些核心,高可靠性场景下,逐渐引入 modality【10】等业界最新成果来提高程序的确定性。这一可选 (opt-in) 的方式,既可以有效降低 MoonBit 的学习曲线,使得更多人可以从 MoonBit 生态中受益,又能保持 MoonBit 的性能,使其在极度苛刻的场景下依然可以胜任。

三、未来展望

MoonBit 原生后端完成了 MoonBit 生态的最后一块拼图。通过原生后端,WebAssembly,JavaScript 后端的支持可以满足绝大多数开发者的多场景需求。未来 MoonBit 团队将会聚焦于开发者的用户体验,提高软件的可靠性,以及培养开发者生态。在开发者体验方面值得注意的是,得益于 MoonBit 编程语言工具链的模块化设计,这三个后端共享了前面绝大部分基础框架,大部分前端的优化都会使三个后端的用户受益。

在开发者生态方面,MoonBit 也很早推出了自己的 包管理工具【11】,让更多 MoonBit 用户可以分享共建 MoonBit 生态库。虽然 MoonBit 才两岁,但是已经吸引到了来自美国的开发者用于商业开发,全球去重用户也已达数万人,这对于一个只有两年发展史的编程语言来说是一个值得称赞的好成绩,期待 MoonBit 团队未来继续努力,给我们带来更多的惊喜。

参考阅读: