0x00
一个非常快速的概览,文章中不涉及深度细节,深度的细节可以在代码中找到: crypto-wasm , node-crypto
随着 2017 年底,四大浏览器厂商全部完成对 WebAssembly 的初步实现,以及 Webpack implementing first-class support for WebAssembly 的消息公布,越来越多的团队在实现需求的时候将 WebAssembly 作为备选技术之一考虑,那么在如今的环境 (Node 8.11.3 LTS,目前阶段没有尝试浏览器), 和相关工具链 (wasm-pack, Emscripten) 下 WebAssembly 的使用体验、性能到底是什么样的状况呢?
0x01 前置背景介绍
如果你已经对 WebAssembly 已经有所了解甚至已经上手试过了,那你可以放心的跳过这一节。这一小节主要介绍一些 WebAssembly 相关的非常基础的概念
WebAssembly 简要来说有以下三个特点:
- 二进制格式,不同于 JavaScript 代码的文本格式
- 标准化,与 JavaScript 一样,实现了 WebAssembly 标准的引擎都可以运行 WebAssembly,不管是在服务器端还是浏览器端
- 快速,WebAssembly 可以充分发挥硬件的能力,以后你甚至可以在 WebAssembly 中使用 SIMD 或直接与 GPU 交互
而 WebAssembly 诞生自浏览器环境,自然需要与 JavaScript 交互,在目前支持 WebAssembly 的环境中使用 WebAssembly 大概需要以下步骤:
-
加载
因为我们需要使用 WebAssembly.instantiate 来实例化一个 WebAssembly 模块,而这个方法只接受 ArrayBuffer 作为第一个参数,所以只能将 .wasm 文件加载为 ArrayBuffer 才能将它实例化
你可以使用 fetch 加载
fetch(url) .then((response) => response.arrayBuffer()) .then((bytes) => { // your bytes here })或者在 Node.js 中使用
fs.readFile加载:const bytes = fs.readFileSync('hello.wasm') -
实例化
WebAssembly.instantiate(bytes, imporObject)这是一个 binding 的过程。
WebAssembly.instantiate方法会将 importObject 对象传递给 wasm ,这样在 wasm 内就可以访问到 imporObject 上的属性和方法,而WebAssembly.instantiate(bytes, imporObject)的返回值则会将 wasm 内暴露的方法交给 JavaScript 调用。const importObject = { imports: { foo: (arg) => console.log(arg), // 可以在 wasm 内调用 }, } WebAssembly.instantiate(bytes, importObject).then((results) => { results.instance.exports.exported_func() // exports 对象上有所有 wasm 暴露的东西 })目前来看这是一个非常冗长和令人费解的过程,你可以在 Using_the_JavaScript_API 找到更多详细的相关描述
-
调用
无参数或者参数类型为数字的时候,可以直接调用 wasm 模块内的方法,但是一旦涉及到其它复杂类型的参数或返回值的时候,就需要直接对内存进行操作才能正常的调用和获取返回值了。
摘自 WebAssembly 系列(四)WebAssembly 工作原理
如果你想在 JavaScript 和 WebAssembly 之间传递字符串,可以利用 ArrayBuffer 将其写入内存中,这时候 ArrayBuffer 的索引就是整型了,可以把它传递给 WebAssembly 函数。此时,第一个字符的索引就可以当做指针来使用。