WebAssembly 简介
本文简单介绍一下 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 等,尝试编译到多平台的。参考:
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 Applets、Adobe 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 |
---|---|
更多应用可参考: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]
Docker 联合创始人在 2019 年说,如果 WASM + WASI 的在 2008 存在的话,他就没有必要去创建 Docker 这种技术方案了,WASM 会是服务端计算的未来技术方案。
这背后,也间接说明了 WASM 对 Docker、以及未来 Serverless/FC/AWS Lambda 等技术方案的重要影响。值得尝试一下。
参考
- ELF32-i386 文件格式简要分析
- X86 Opcode and Instruction Reference Home
- 80386 模拟器源码分析 (2)
- ARM Assembly on MacOS
- Host/Target specific installation notes for GCC
- Linux assemblers: A comparison of GAS and NASM
- https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions
- https://en.wikipedia.org/wiki/Java_applet
- https://en.wikipedia.org/wiki/NPAPI#Support/deprecation
- https://en.wikipedia.org/wiki/Asm.js
- Photoshop is now on the web
- ChromeOS
- https://en.wikipedia.org/wiki/WebAssembly
- https://github.com/appcypher/awesome-wasm-runtimes
- Webassembly for the Java geek
- Awesome WebAssembly Applications