跳到主要内容

使用

开发使用

  • 首先安装 wabt.js
    • WABT 的 JavaScript 版本,即 WebAssembly Binary Toolkit。是 WABT 到 WEB 的端口,帮助用户使用 JS API 操纵 WebAssembly 模块。
    • 创建 wat 文件,wasm 文件的一种 AST 表示方式

wat 文件

(module
(func $add(param $l i32)(param $r i32)(result i32)
get_local $l
get_local $r
i32.add
)
(export "add" (func $add))
)
  • 通过 wabt 将 wat 文件编译成 wasm 文件
const { readFileSync, writeFileSync } = require("fs");
require("wabt")().then((wabt) => {
const input = "./add.wat";
const output = "./add.wasm";
const wasmModule = wabt.parseWat(input, readFileSync(input, "utf-8"));
const { buffer } = wasmModule.toBinary({});
writeFileSync(output, Buffer.from(buffer));
});

html 中使用

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<div id="result"></div>
</body>
<script>
function getAdd() {
return fetch("./add.wasm")
.then((res) => res.arrayBuffer())
.then((wasm) => {
return WebAssembly.instantiate(wasm, {
env: {
memory: new WebAssembly.Memory({
initial: 256,
}),
table: new WebAssembly.Table({
initial: 2,
element: "anyfunc",
}),
abort: () => {
throw "abort";
},
},
});
})
.then((result) => {
const result = result.instance.exports.add(20, 89);
document.getElementById("result").innerHTML = result;
console.log(result);
});
}
getAdd();
</script>
</html>

Memory 对象的 buffer 属性是一个可调整大小的 ArrayBuffer,其内存储的是 WebAssembly 实例所访问内存的原始字节码。因为 ArrayBuffer 是一个 JS 对象,这意味着 JavaScript 也可以获取到这个 memory 中的字节。所以通过这种方式, WebAssembly 和 JavaScript 可以共享内存,并且相互传值。另外 Memory 本身也只是个 JS 对象,它会被垃圾回收器跟踪;并且 ArrayBuffer 本身存在边界,可以通过防止浏览器级内存泄漏并提供内存隔离,使事情变得更安全。。

Table 是存在于 WebAssembly 内存之外的数组。它的值是对函数的引用。这些引用包含内存地址,但由于它不在 WebAssembly 的内存中,WebAssembly 不能看到这些地址。但它确实可以访问数组索引。。 从上面的例子可以看到 wasm 文件使用很方便,并且提供了许多 api 给开发者调用。但是,直接写指令文本难度较高,学习成本较大,因此建议还是选择我们更熟悉的写法来编写;官方推荐的是 C/C++。

运行速度对比——以斐波纳切为例

  • C 语言实现斐波纳切数列
#include <stdio.h>
int fibonacci(int n)
{
if (n <= 2)
{
return 1;
}
else
{
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
  • 使用 Emscripten 编译器,将 C 编译为 wasm,Emscripten 安装教程

emcc fib.c -Os -s WASM=1 -s SIDE_MODULE=1 -o fib.wasm

  • 在 HTML 中进行运行对比,计算斐波纳切的第 46 项(47 项会溢出),对比效率。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
js:
<div id="js"></div>
webAssembly:
<div id="webAssembly"></div>
</body>
<script>
const beg1 = new Date();
function fibonacci_js(n) {
if (n == 1 || n == 2) {
return 1;
}
return fibonacci_js(n - 2) + fibonacci_js(n - 1);
}
const sum = fibonacci_js(46);
document.getElementById("js").innerHTML = new Date() - beg1;
console.log("fibonacci(46) in js: ", new Date() - beg1, sum);

function getAdd() {
let beg2 = new Date();
return fetch("./test.wasm")
.then((res) => res.arrayBuffer())
.then((wasm) => {
beg2 = new Date();
return WebAssembly.instantiate(wasm, {
env: {
memoryBase: 0,
tableBase: 0,
memory: new WebAssembly.Memory({ initial: 0, maximum: 0 }),
table: new WebAssembly.Table({
initial: 2,
maximum: 2,
element: "anyfunc",
}),
abort: () => {},
},
});
})
.then((result) => {
const sum = result.instance.exports._fibonacci(46);
console.log("fibonacci(46) in WebAssembly: ", new Date() - beg2, sum);
document.getElementById("webAssembly").innerHTML = new Date() - beg2;
});
}
getAdd();
</script>
</html>
  • 结果 WebAssembly 的计算效率明显高于 js,大约提升了两倍。

WebAssembly 应用

  • Figma — 基于浏览器的多人实时协作 UI 设计工具, Asm.js-> WebAssembly,效率又提升了 3 倍。
  • Google Earth — 支持各大浏览器的 3D 地图,而且运行流畅 WebAssembly 的缺点 WebAssembly 标准虽然已经定稿并且得到主流浏览器的实现,但目前还存在以下问题:
  • 浏览器兼容性不好,只有最新版本的浏览器支持,并且不同的浏览器对 JS WebAssembly 互调的 API 支持不一致;
  • 生态工具不完善不成熟,目前还不能找到一门体验流畅的编写 WebAssembly 的语言,都还处于起步阶段;
  • 学习资料太少,还需要更多的人去探索去踩坑。
  • 而且 WebAssembly 目前还存在着一些问题,比如容易受到恶意攻击,具体可以参考https://www.virusbulletin.com/virusbulletin/2018/10/dark-side-webassembly/。 目前来说 WebAssembly 的应用场景还是比较少的,而且其成熟性还不够,因为不到万不得已,WebAssembly 还是不要应用于项目当中。