WebAssembly是什么
WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable compilation target for programming languages, enabling deployment on the web for client and server applications.
—— webassembly.org/
从官网释义来看,Wasm是基于栈式虚拟机的二进制指令格式。作为高级编程语言的编译目标,它具有强可移植性,允许客户端、服务端应用部署在web上。
诞生背景
JavaScript缺陷
先来看看 JavaScript 代码在 V8 引擎中是怎么工作的[1]。
- JavaScript 进入 Parser(解析器),Parser 会将其转化成 AST(抽象语法树)
- 根据抽象语法树,Interpreter(解释器)会生成引擎能够直接阅读和执行的 Bytecode(字节码)
- 接着,Compiler(编译器)将字节码逐行翻译成可高效执行的 Machine Code(机器码)
- 对于执行次数较多的函数,引擎会将其编译成 Machine Code 并进行 JIT 优化(Just-In-Time编译) ,下次再执行这个函数的时候,就会直接执行编译好的 Machine Code。
不像 C++、JAVA 这些强类型语言,JavaScript 是一种弱类型的语言,需要在运行过程中动态编译。一个 JS变量,可能在上一秒是个Number,下一秒就变成了Array,这种灵活性使得代码在引擎中的优化有限。
asm.js
既然 JavaScript 存在因为弱类型带来的天生缺陷,那么,是否可以通过特殊写法使其具有隐式类型推断功能,来对代码性能进行优化呢?鉴于此,WebAssembly的前身,asm.js 出现了。
举个例子,下述代码中,|0
会使引擎推断出变量 a
是一个整数。
let a = 1.2 | 0
console.log(a) // 1
但是,asm.js 并不能解决所有问题:
- asm.js 是 JavaScript 的严格子集,开发者无法使用 JS 的所有功能;
- asm.js 同样是作为一种编译目标存在的(Emscripten 可将 C++/C 编译成 asm.js),虽然语法上比 WebAssembly 清楚得多,但是靠人工手写还是有一定难度;
- 此外,无论 asm.js 对类型推导的问题做得再好,它始终逃不过要经过 Parser 和 Interpreter 这两步,而这两步是 JS 代码在引擎执行过程中最耗时的两步。
WebAssembly出现
鉴于 asm.js 存在的问题,WebAssembly 跳过了 Parser 和 Interpreter 这两步,作为编译后的字节码(同样通过 Emscripten 编译)直接在浏览器中运行,极大地提高了代码在浏览器中的运行速度。下面是一张示意图。
除了运行速度之外,Wasm 还具有以下优点:
- 特有的二进制格式有效地减小了包体积,进一步提升了浏览器的加载速度;
- 支持将 10+ 种编程语言(C/C++, Rust, Go等)编译成 Wasm,这意味着可以借鉴其他语言的生态,使其他语言下的标准类库在浏览器中直接使用;
- 安全的沙箱运行环境,在浏览器中同样支持同源策略等权限限制;
- 各大主流浏览器厂商(Firefox, Chrome, Safari, Edge等)相继支持 WebAssembly,并不断完善标准。目前,WebAssembly 已成为继 HTML、CSS、JavaScript 之后的第 4 种 Web 语言。
实操演示
先大致描述下 Wasm 程序的工作过程:
- 使用其他语言(C/C++, Rust, Go等)编写程序,并通过各自的工具链编译为 WebAssembly 文件(.wasm格式);
- 通过 fetch、XMLHttpRequest 等获取 .wasm 文件,得到一串 ArrayBuffer;
- 将 ArrayBuffer 编译为浏览器可执行的模块,并实例化;
- 调用从 Wasm 模块内导出的方法,完成所需操作。
下面来看一个实际的例子:
环境安装
- Git:用于拉取 Emsdk 等
- CMake:编译 C 语言
- Host system compiler(宿主环境编译工具):Linux - GCC、OSX - Xcode、 Win - VS Code
- Python 2.7+
- Emscripten SDK:Wasm 编译器
git clone https://github.com/juj/emsdk.git
cd emsdk
./emsdk.bat install latest
./emsdk.bat activate latest
./emsdk_env.bat // 配置环境变量,每次重新登录或者新建shell窗口,都要执行一次这行命令
Emscripten编译C语言文件
int multiply(int a,int b){
return a * b;
}
执行 emcc index.c -Os -s WASM=1 -s SIDE_MODULE=1 -o math.wasm
,生成 math.wasm。
加载调用
<html>
<head>
<script>
// Check for wasm support.
if (!('WebAssembly' in window)) {
alert('you need a browser with wasm support enabled :(');
}
function loadWebAssembly(filename, imports = {}) {
return fetch(filename)
.then(response => response.arrayBuffer())
.then(buffer => {
imports.env = imports.env || {}
Object.assign(imports.env, {
memoryBase: 0,
tableBase: 0,
__memory_base: 0,
__table_base: 0,
memory: new WebAssembly.Memory({ initial: 256, maximum: 256 }),
table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' })
})
return WebAssembly.instantiate(buffer, imports)
})
.then(result => result.instance)
}
loadWebAssembly('math.wasm')
.then(instance => {
const { multiply } = instance.exports
var button = document.getElementById('run');
button.value = 'Call wasm multiply';
button.addEventListener('click', function() {
alert('60 * 11 = ' + multiply(60, 11))
}, false);
})
</script>
</head>
<body>
<input type="button" id="run" value="waiting for WebAssembly..."/>
</body>
</html>
用 http-server
在本地启动一个静态资源服务器并访问页面,可看到在点击按钮后,弹窗显示 660
,说明我们成功地在 JS 中调用了 C 文件的方法。
应用场景
目前,Wasm 主要有以下几类应用场景:
1 性能
对于那种计算密集型,或者对性能要求高的场景,如:图像/视频解码、图像处理、3D/WebVR/AR 等,Wasm 的优势明显。如:
- Bilibili:当你的视频还在上传中,已经可以自由选择AI推荐的封面。这里采用了 webassembly+AI 的前端整合。Wasm 负责读取本地视频,生成图片;tensorflow.js 负责加载 AI 训练过的 model,读取图片并打分。
- Figma:一个基于浏览器的协作式 UI 设计工具,作用类似于 Sketch。
2 复用
Wasm 可将多种高级语言编译为二进制码,这使在浏览器中复用其他语言的生态成为可能。如:
- Unity 教程游戏的 Wasm 版本:webassembly.org.cn/demo/
- 利用 OpenCV 库实现浏览器端实时头像捕捉 & 图片处理:vinissimus.github.io/opencv-js-w…
3 跨平台
WASI(WebAssembly System Interface)的出现,使得 WebAssembly 也能应用在非浏览器的环境中,这给了 Serverless 的更大规模落地有了更多的想象空间[4]。
发展现状
开发语言
目前,开发者们最喜爱的 Wasm 开发语言有:Rust, C++, AssemblyScript等。其中,AssemblyScript 是专为 Wasm 设计的类 TS 的语言,方便前端开发者上手。
应用类型
现阶段的 WebAssembly 应用以 Web 应用开发为主,但具体是哪些类型的 Web 应用还有待调研。此外,在游戏开发、Serverless应用、容器化、音视频处理、IoT等方面也有较多应用。
参考
- [1] 浏览器是如何工作的:Chrome V8让你更懂JavaScript
- [2] WebAssembly完全入门 - 了解wasm的前世今生
- [3] webassembly.org/
- [4] Serverless Days 2020 Looks at Future of Serverless Architecture
- [5] The State of WebAssembly 2021
最后
搜索公众号Eval Studio,关注我们,获取更多动态