本文简单介绍一下 WASM 的出现背景,以及它用来解决什么样的问题。了解其历史脉络,来更为充分理解其未来会走向何处。

机器码 Machine Code

一开始的计算程序,都是通过二进制机器码编写,比如一个 1 + 2 ,并将加法结果输出作为返回值的程序,在现在 i386 中的 elf 可执行二进制编码如下,参考:ELF32-i386 文件格式简要分析

$ xxd -s 0x1000 -l 0x11 -b ./plus
00001000: 01100110 10111000 00000001 00000000 01100110 10111011  f...f.
00001006: 00000010 00000000 01100110 00000001 11000011 01100110  ..f..f
0000100c: 10111000 00000001 00000000 11001101 10000000           .....           

对应的 16 进制如下:

$ xxd -s 0x1000 -l 0x11 ./plus
00001000: 66b8 0100 66bb 0200 6601 c366 b801 00cd  f...f...f..f....
00001010: 80   

比如:b8/bb,分别将跟随后续的 16 字节作为立即数(数字 1 和 2 )直接移入 eax/ebx

前缀 0x66 参考:https://zhoukekestar.github.io/notes/2023/01/07/i386-2.html

汇编 Assembly

随着计算机程序的快速应用,程序越来越大、功能越来越复杂,手写机器码变得越来越低效,所以汇编语言应用而生,同样的上述 1 + 2 的程序,汇编程序如下(nasm 汇编),参考:ELF32-i386 文件格式简要分析

   mov   ax, 1    ; ax 寄存器放入 1
   mov   bx, 2    ; bx 寄存器放入 2
   add   bx, ax   ; 执行加法,并将结果放入 bx 寄存器

   ; 执行程序退出
   mov	ax,1     ; 中断号 1
   int	0x80     ; 执行内核中断,中断号为 ax 的 1

通过 objdump 的反编译结果如下:

$ objdump -S ./plus
./plus:     file format elf32-i386
Disassembly of section .text:
08049000 <.text>:
 8049000:       66 b8 01 00             mov    $0x1,%ax
 8049004:       66 bb 02 00             mov    $0x2,%bx
 8049008:       66 01 c3                add    %ax,%bx
 804900b:       66 b8 01 00             mov    $0x1,%ax
 804900f:       cd 80                   int    $0x80

CPU ISA

随着计算机行业的快速发展,各种不同的 CPU 厂商先后推动了面向不同业务场景的 CPU 指令集架构,其中较为知名的有:IA-32(Intel)、x86-64(AMD)、ARM(Qualcomm/Apple M1)、MIPS(Loongson)、RISC-V(IOT) 等。

还是 1 + 2 的例子,ARM 的汇编如下,参考:ARM Assembly on MacOS

.global _main
_main:
	mov	x0, #1
	mov	x1, #2
	add	x0, x0, x1
	ret

对应的机器码如下:

|-----  运行时代码 ------------------|   |- 二进制-|   |------ 前 32 位的二进制指令 --------|
0x100003fa8 <+0>:  mov    x1, #0x1      d280 0021   11010010 10000000 00000000 00100001
0x100003fac <+4>:  mov    x2, #0x2      d280 0042   11010010 10000000 00000000 01000010
0x100003fb0 <+8>:  add    x0, x1, x2    8b02 0020   10001011 00000010 00000000 00100000
0x100003fb4 <+12>: ret                  d65f 03c0   11010110 01011111 00000011 11000000

可以发现,汇编语言一般是和 CPU 架构绑定的,但也有 GAS、NASM 等,尝试编译到多平台的。参考:

Linux assemblers: A comparison of GAS and NASM

CPU-Specified Target

由于汇编也是和 CPU 绑定的,于是慢慢开始发展起了以 C/C++ 为代表的高级语言,将上述的机器码、汇编的逻辑都做磨平,比如以 GCC 为代表的,支持大部分现有的 CPU 架构,Host/Target specific installation notes for GCC(PS:国产CPU 架构指令集,loongson 龙芯目前也支持了)

所以,在较长的时间里,一般都会写好高级语言,然后通过编译工具,编译到不同的 CPU 架构上,来适配不同的 CPU ISA。当然编译器,还需要做非常多的优化,最基础的,就是寄存器的优化,比如寄存器的数量、位宽等等。

JVM

因为每到一个 CPU 架构平台,程序就需要重新编译一次,能不能程序编译一次,然后就能适配到不同的平台呢?很自然地,就出现了一种从 AOT(Ahead Of Time) 到 JIT(Just InTime) 的方案,最具代表的,就是 JVM。

高级语言,只需要编译到 Java Bytecode,然后通过 JVM 就能运行起来。JVM 本身用 C 写,然后编译到不同的平台,但 Java ByteCode 应用只需要编译一次,就能通过 JVM 在所有平台都运行起来,所以叫做 Java Virtual Machine,相当于虚拟了一台机器,这台机器的 CPU ISA 就是 Java Bytecode。其指令集,就叫 Java Bytecode Instruction。运行的过程中,由 JVM 实时(JIT)地将虚拟指令映射为真实的机器指令。

如有必要,还可以使用类似 Hotspot 技术,将部分热点代码在运行时编译为机器代码,做缓存来达到更高的性能。

WebBrowser

浏览器从某种意义上讲,也是一种 Virtual Machine,一种为专门写界面级应用的 VM,写完一次,能在所有平台运行。

这里说 WebBrowser 为 Virtual Machine 的另外一个证明是,以 ChromeOS 为代表的操作系统及其笔记本(还有其他以浏览器作为操作系统的手机等等)的出现。

参考:ChromeOS

所以,作为 GUI 应用级的 VM,其他语言也当仁不让,特别是以 Java AppletsAdobe Flash Player 为代表的 VM 曾一度将 WebBrowser “架空”。但随着其上诉技术方案所依赖的底层接口,Netscape Plugin Application Programming Interface (NPAPI) 的种种问题,包括:接口的老化、安全问题、以及 HTML5 等新技术的采用,它们也睡着慢慢退出历史舞台。参考:NPAPI

但是 NPAPI 背后所催生的诉求,如:高性能的计算等,并没有因此减少,随着 Web 应用越来越大型化,此类需求反而越来越强烈。

asm.js

伴随着 NPAPI 的慢慢弃用,以 asm.js为代表的 JavaScript 子集,可以将其他语言(如C)转成 JS,并能做高效地执行。参考:https://en.wikipedia.org/wiki/Asm.js,如 C 中的 整数+1 函数可转换为:

function f(i) {
  i = i|0;
  return (i + 1)|0;
}

上述 JS 代码,通过位运算,将 JS 中的 i 变量,转换为 32 位整型,由此就可以避免 JS 运行过程中,对 i 变量的类型判断、运算过程中的浮点判断等等,从而也给 JS 引擎的执行优化,提供了更多的依据和空间。当然,更多的性能优化,则需要更多的规则及其相关的浏览器引擎侧的支持,Firefox 是对 asm.js 优化较好的浏览器,也在积极推动相关规范和实现的落地。

WebAssembly

伴随着 WebBrowser 在 PC 中越来越占有的主导地位(PC 客户端的日渐衰弱,以及浏览器及其强大的跨端、内容资讯等),除少量本地的大型应用外,CS 架构也慢慢往 BS 架构迁移。

大量的语言对编译到浏览器有了更为强烈的诉求,如:C、C++、Python、Ruby等,另外还包括一些基础工具,比如:QT、OpenGL、SQLLite 为代表的项目。

到目前为止,本地的大型应用也慢慢开始有了 Web 版本,特别是以 Photoshop 和 AutoCAD 为典型的出现。

Photoshop AutoCAD
Image Image

更多应用可参考:Awesome WebAssembly Applications

JVM vs WASM

随着 Java 在 PC 客户端(如:Swing)、浏览器端(如:Java Applet)的失利,Java 目前仅在服务端较为流行。

与此同时,随着 WebAssembly 在浏览器端的崛起,WASM + WASI 的组合也在其他领域攻城略地,包括客户端、服务器端。特别是在服务器端,各种 Runtime 百家争鸣,参考:awesome-wasm-runtimes

WebAssembly System Interface (WASI) is a simple interface (

ABI and API) designed by Mozilla intended to be portable to any platform.[95] It provides POSIX-like features like file I/O constrained by capability-based security.[96][97] There are additional proposed ABI/APIs.[98][99]

在这时,JVM 和 WASM 又产生了一种神奇的碰撞,WebAssembly 由 Web + Assembly 组成,Web 代表通用跨多端,Assembly 则代表着底层、性能。所以两者的目标非常神似:以字节码的通用汇编,编译一次,跨多端运行。

以下是不完整对比列表:

  Performance Runtime Langs Sandbox GC Alibaba
MiddleWare
JVM Higher ISA
JIT/Hotspot?
Stack-based Machine Kotlin、Groovy ClassLoader Supported Great
WASM Lower ISA Structured Stack-based Machine C/C++、Rust、Go VFS/Socket/etc… Not Supported Not so Good

参考:Webassembly for the Java geek

小结

最后,以我印象最为深刻的一句话作为小结:

Solomon Hykes [fr], a co-founder of Docker, wrote in 2019, "If WASM+WASI existed in 2008, we wouldn't have needed to create Docker. That's how important it is. WebAssembly on the server is the future of computing."[100]

参考:https://en.wikipedia.org/wiki/WebAssembly

Docker 联合创始人在 2019 年说,如果 WASM + WASI 的在 2008 存在的话,他就没有必要去创建 Docker 这种技术方案了,WASM 会是服务端计算的未来技术方案。

这背后,也间接说明了 WASM 对 Docker、以及未来 Serverless/FC/AWS Lambda 等技术方案的重要影响。值得尝试一下。

参考

  1. ELF32-i386 文件格式简要分析
  2. X86 Opcode and Instruction Reference Home
  3. 80386 模拟器源码分析 (2)
  4. ARM Assembly on MacOS
  5. Host/Target specific installation notes for GCC
  6. Linux assemblers: A comparison of GAS and NASM
  7. https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
  8. https://en.wikipedia.org/wiki/Java_applet
  9. https://en.wikipedia.org/wiki/NPAPI#Support/deprecation
  10. https://en.wikipedia.org/wiki/Asm.js
  11. Photoshop is now on the web
  12. ChromeOS
  13. https://en.wikipedia.org/wiki/WebAssembly
  14. https://github.com/appcypher/awesome-wasm-runtimes
  15. Webassembly for the Java geek
  16. Awesome WebAssembly Applications