WASM 的官网 webassembly.org 是这样定义的[2]:
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.
首先 WASM 是一组指令集格式,这组指令集可以运行在一个特定的基于栈的 VM 上。VM 一般有基于寄存器(erlang)的和基于栈(java)的,WASM 选择了后者,很大一个原因是为了产生更小的程序代码。WASM 的前身是 asm.js,当时 Mozilla 的工程师尝试着把大型的 C/C++ 项目编译成 javascript 的一个隐含类型的,为 JIT 编译器高度优化的子集,结果得到了在浏览器里近乎接近原生应用的效率。之后,Mozilla,Google,Microsoft,Apple 四巨头坐在一起,共同定义和开发了 WASM 技术,并在各自的浏览器中进行支持。2019 年 12 月,WASM 正式被接纳为 W3C 推荐标准[3][4],成为浏览器中除了 HTML/Javascript/CSS 之后,第四个原生的可执行语言。
WASM 在设计之初就和 javascript 是并行的语言,它的出现不是为了取代 javascript,相反,javascript 还为 WASM 代码的执行起粘合作用。WASM 的强项在于可以将 javascript 生态圈之外的已有代码,尤其是 C/C++,搬运到 web 上,这样能够大大丰富 web 的生态圈,使得原本难以用 javascript 重写的很多系统可以被放在 web 上,比如 vim[5]。
WASM 最早的实现是 Enscripten 编译器,它使用 LLVM,把 C/C++ 代码编译成 WASM,理论上来说,任何使用 LLVM 的编译器都可以使用其支持 WASM。早期的 Rust 通过 Enscripten 支持 WAS,后来提供了自己的 wasm32-unknown-unknown 更好地支持 WASM。Enscripten 除了可以把 C/C++ 代码编译成 WASM 外,它还模拟了 Unix 的运行环境,这让很多的 C/C++ 代码可以做进行少量修改(主要是编译脚本)就可以编译成 WASM。然而,一旦你使用了多线程(WASM 目前还不支持),或者特殊的设备驱动,或者阻塞操作(如 block wait),那么代码需要做不少修改。此外,传统的桌面和命令行软件,其和用户交互的部分也需要修改。下图是 vim.wasm 对 Vim 的交互做的改动:
其整个编译过程如下:
尽管 WASM 已经发展了有五年之多,目前,对 WASM 真正具备完整的,有意义的支持的语言也就是 C/C++/Rust。其它语言的支持要么是残缺的,要么是不可用的。
我们拿 golang 为例。一个基本的 hello world 编译成 WASM 要 2M 多(估计主要是因为 golang 运行时的代码):
虽然我们可以用很多 hack 把编译结果弄到 1M 多,但这个结果对 web 来说是不可接受的。此外,golang 还没有完全兼容 WASM 1.0 协议,所以不算可用[7]。TinyGo 对 WASM 的支持看上去很有前途,但仅仅因为 TinyGo 不支持 reflection,就自动过滤了 golang 生态圈一大票依赖,比如 protobuf。很多时候,语言的生态本身要比其语法重要得多。语法不难复刻,但生态是需要很长的时间成长起来的。
好在 WASM 目前在飞速发展,有很多功能在讨论和实现之中。比如说:GC 的支持和多线程的支持。有了这两个利器,尤其是 GC 的支持,可以让很多依赖 GC 的语言产生更小的字节码(golang 喜极而泣),这样,WASM 将来可以被更多的语言支持。
有同学拿 awesome-wasm-langs[8] 来反驳我,说:不止 C/C++/Rust,现在已经有几十种语言支持 WASM 了,比如 Python。这个列表的确唬人,但仔细看,比如 pyodide,明明是 python 及其科学计算相关的库被编译成了 WASM 啊?这其实是把用来写 python 解释器的 C 代码编译成 WASM,然后可以执行 Python 代码而已,并不是把 Python 代码编译成 WASM —— 当然如果你非要较真这 TM 就是 Python 支持 WASM 我也无话可说。pyodide 解压下来有 300M,其 WASM 主体也有 13M。这显然不是给正常的 web 使用场景准备的。
我无意贬低 pyodide,这是一个很好的在线运行数据科学家工具集的好工具,就像 unreal 引擎运行在浏览器一样,对特定需求的受众有很强大的吸引力(所以它们不介意加载速度)。但它并不意味着你写一段 hello world,可以通过 pyodide 得到一个能够单独加载的 WASM,这是两回事。如果一门语言对 WASM 的支持是这样子支持,那么的确,在下输了,所有语言都马上能「支持」WASM。
|
|