WebAssembly 是一种新的编码方式,可以在现代的网络浏览器中运行 — 它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C++ 等语言提供一个编译目标,以便它们可以在Web上运行。
WebAssembly 被设计为可以和 JavaScript 一起协同工作 — 通过使用 WebAssembly 的 JavaScript API,你可以把 WebAssembly 模块加载到一个 JavaScript 应用中并且在两者之间共享功能。这允许你在同一个应用中利用 WebAssembly 的性能和威力以及 JavaScript 的表达力和灵活性。
WebAssembly 如何在浏览器中运行,需要了解几个关键概念,这些概念都是一一映射到了WebAssembly的JavaScript API中。
postMessage()
函数)进行共享。一个模块能够像一个 ES2015 的模块一样声明导入和导出。Memory与Table区别: WebAssembly Table 是一个可变大小的带类型(唯一合法的是函数类型)的引用数组,其中的引用可以被 JavaScript 和 WebAssembly 代码存取。然而,Memory 提供的是一个可变大小的带类型的原始字节数组。所以,把引用存储在 Memory 中是不安全。 在C/C++的原生实现中,函数指针是通过函数代码在进程的虚地址空间的原始地址表示的,并且由于前面提到的安全原因,它是不能被直接存储在线性内存(Memory)中的。取而代之的是,函数引用被存储在 Table 之中。它们的整数索引可以存储在线性内存(Memory)中并进行传递。
C/C++ 和 Javascript 区别:
在线 WASM 汇编程序: WasmFiddle、WasmFiddle++、WasmExplorer
Emscripten工具能够将一段C/C++代码,编译出:
Emscripten 是一个 LLVM(底层虚拟机) 生成 JavaScript 的编译器. 它采用 LLVM的字节码 (例如,使用 Clang 从 C/C++ 或者从其他语言生成的字节码) 并将其编译成可在 Web 上面运行的 JavaScript
Emscripten生成的代码(其默认输出格式为 asm.js ,这是 JavaScript 的高度优化子集「变量一律都是静态类型,并且取消垃圾回收机制」)在许多情况下可以以接近原生的速度执行。
Asm.js 是一个规范,它定义了高度可优化的 JavaScript 严格子集。仅允许诸如 while、if、数字、顶级命名函数和其他简单构造之类的东西。它不允许对象、字符串、闭包以及基本上所有需要堆分配的内容。Asm.js 代码在许多方面都类似于C,但是它仍然是完全有效的 JavaScript,可以在所有当前引擎中运行。
一旦 JavaScript 引擎发现运行的是 asm.js,就知道这是经过优化的代码,可以跳过语法分析这一步,直接转成汇编语言。另外,浏览器还会调用 WebGL 通过 GPU 执行 asm.js。这些就是 asm.js 运行较快的原因。
当前还没有内置的方式让浏览器为你获取模块。当前唯一的方式就是创建一个包含你的 WebAssembly 模块二进制代码的 ArrayBuffer
并且使用 WebAssembly.instantiate()
编译它。
#include
float add (float a, float b) {
return a + b;
}
通过 WasmExplorer 工具(也可以使用上述的 Emscripten)编译后:
(module
(table 0 anyfunc)
(memory $0 1)
(export "memory" (memory $0))
(export "add" (func $add))
(func $add (; 0 ;) (param $0 f32) (param $1 f32) (result f32)
(f32.add
(get_local $0)
(get_local $1)
)
)
)
示例:
<label for="num1"><input id="num1" type="text">label>
<label for="num2"><input id="num2" type="text">label>
<input id="compute" type="button" @click="add" value="计算">input>
<label id="result">label>
<script>
const $ = (arg) => document.querySelector(arg)
// 创建内存示例(initial:初始大小;maximum:最大大小)
const memory = new WebAssembly.Memory({ initial: 256, maximum: 256 });
const importObj = {
env: {
abortStackOverflow: () => { throw new Error('overflow'); },
table: new WebAssembly.Table({ initial: 0, maximum: 0, element: 'anyfunc' }),
tableBase: 0,
memory: memory,
memoryBase: 1024,
STACKTOP: 0,
STACK_MAX: memory.buffer.byteLength,
}
};
fetch('./add.wasm').then(response =>
// 1. 返回一个可以解析为带类型数组的promise
response.arrayBuffer()
).then(bytes =>
// 2. 编译和实例化带类型数组
WebAssembly.instantiate(bytes, importObj)
).then(wa => {
$('#compute').addEventListener('click', () => {
// 3. 使用通过 WebAssembly.Instance.exports 属性导出的特性
let result = wa.instance.exports.add(+$('#num1').value, +$('#num2').value)
$('#result').innerHTML = result
})
});
script>