关于 WebAssembly 的一些疑惑总结
什么是 WebAssembly
WebAssembly 是 W3C 规范定义的一个编码方式规范,2017年由 Firefox 和 Chrome 等实现了其MVP(最小可用)版本,WebAssembly 容器本质上是一个堆栈式虚拟机。
常见的虚拟机分为:堆栈式、寄存式、累加器式,主要区分在如何处理数据和指令上
不同语言(C、C++、Go...)可以按照 WebAssembly 的标准编译为符合运行标准的程序,如果你愿意你可以手写一个 WebAssembly 程序(二进制),运行在 WebAssembly 容器中,例如:
WebAssembly.compile(new Uint8Array(`
00 61 73 6d 01 00 00 00 01 0c 02 60 02 7f 7f 01
7f 60 01 7f 01 7f 03 03 02 00 01 07 10 02 03 61
64 64 00 00 06 73 71 75 61 72 65 00 01 0a 13 02
08 00 20 00 20 01 6a 0f 0b 08 00 20 00 20 00 6c
0f 0b`.trim().split(/[\s\r\n]+/g).map(str => parseInt(str, 16))
)).then(module => {
const instance = new WebAssembly.Instance(module)
const { add, square } = instance.exports
console.log('4 + 8 =', add(4, 8))
console.log('5^2 =', square(5))
console.log('(4 + 8)^2 =', square(add(4 + 8)))
})
什么是虚拟机
这里提到的虚拟机指的是进程的虚拟机,虚拟机其实就是模拟物理机器的,而进程虚拟机其实就是去模拟CPU的一些操作,比如取指令,解析指令,执行指令,存储数据等,不同类型的虚拟机实现最大的区别在于操作数的存取和操作结果的存放方法是不同的。
WebAssembly 程序本质上是什么
可以运行在 WebAssembly 容器的程序,以二进制形式存在,运行时需要通过容器编译后才能执行,以满足不同计算机架构的执行环境(可以类比Java的字节码)。
WASM 文件结构 = TypeSection/StartSection/GlobalSection/CustomSection/ImportSection/ExportSection/FunctionSection/CodeSection/TableSection/ElementSection/MemorySection/DataSection。TypeSection、FunctionSection、CodeSection 这三个是必须的,分别的意义是
Section 的结构是标准的程序架构,可以回忆下编译原理。
- TypeSection 描述函数的签名
- FunctionSection wasm里函数的索引
- CodeSection 函数方法体
其中还有很重要的1个 Section 是 ExportSection 定义输出functions/memories/tables/globals available。
WASM 和 WebAssembly 的区别
一个是简写,一个是全称
WebAssembly 运行时和 JavaScript VM Runtime 有什么关系
我们知道 JavaScript 本身也是运行在一个虚拟机上,我们也知道 WebAssembly 的前身是 asm.js,JavaScript引擎对 asm.js 代码也做了编译期优化,将 JavaScript 代码优化、编译为机器码提高执行效率。同样的方式编译 wasm 的逻辑集成在 JS 引擎中,可以在源码中找到。
同样的 WASM 还需要经过编译才能执行,WASM 的运行时和 JS 的运行时是在同一个 Context 中的,本质上公用了一个编译器后端。浏览器引入了一个新的编译器将 wasm 二进制指令编译为 JavaScript 解析器能理解的字节码。2018年 V8 引入了 LiftOff 编译器来处理 WASM 编译工作,编译为 turboFan 能处理的字节码,用来优化代码
名词解释:
Baseline Compiler: baseline compiler is to generate bytecode or machine code as fast as possible. This output code (machine code or intermediate code) however is not very optimized for a processor, hence it's very inefficient and slow in runtime.
Optimization compiler: produces an efficient code but it takes a much longer time to do so.
Baseline Compiler 是会尽快的生成为优化的 ByteCode;
Optimization Compiler 会优化 ByteCode,但是会花费更多的时间;
由上面的图可以看出来
- JavaScript 代码执行会经过 Baseline Compiler 生成未优化的 ByteCode 再给解释器 Interpreter 编译为机器码
- JavaScript 代码执行会经过 Baseline Compiler 生成未优化的 ByteCode 也会通过 TurboFan 优化代码编译为机器码
- WebAssembly 二进制源码会经过 Liftoff 生成未优化的 ByteCode,再给到 TurboFan 优化代码编译为机器码
- WebAssembly 和 JavaScript 的编译后端是共享的,WebAssembly 的尽头是和架构相关的机器码
WAT 和 WASM 的关系
WASM二进制文件是不可读的,WebAssembly Text Format(WAT)是另外一种输出格式,以类文本的方式展示输出,WAT类似于WASM的 source-map
WebAssembly 容器与 JavaScript VM 何如交互
我们知道WebAssembly 的内存是映射到 JavaScript 的对象 ArrayBuffer 上的,这意味着 JavaScript 有足够的能力可以去操作内存里的字节。
后续文章总结
参考资料
- 《深入浅出 WebAssembly》
- https://medium.com/jspoint/the-anatomy-of-webassembly-writing-your-first-webassembly-module-using-c-c-d9ee18f7ac9b
- https://hacks.mozilla.org/2017/02/creating-and-working-with-webassembly-modules
- https://hacks.mozilla.org/2017/07/creating-a-webassembly-module-instance-with-javascript/
- https://rsms.me/wasm-intro
- https://hacks.mozilla.org/author/lclarkmozilla-com/
- https://stackoverflow.com/questions/59638327/what-is-a-baseline-compiler
- https://zhuanlan.zhihu.com/p/338265761
- https://blog.csdn.net/weixin_33878457/article/details/90624317