如何使用 WebAssembly 和 JS构建高性能应用程序( 二 )

上述方法的缺点之一是这些方法不能直接访问字节码 , 因此在编译 / 实例化 wasm 模块之前 , 需要采取额外的步骤将响应转换为 ArrayBuffer 。 相比之下 , 我们可以使用 WebAssembly.compileStreaming/WebAssembly.instantiateStreaming 方法来实现上述功能 , 其优点是可以直接访问字节码 , 而无需将响应转换为 ArrayBuffer 。
let exports;WebAssembly.instantiateStreaming(fetch('sample.wasm')).then(obj => {exports = obj.instance.exports;})应注意 , WebAssembly.instantiate 和 WebAssembly.instantiateStreaming 会返回实例以及已编译的模块 , 这些实例可用于快速启动模块的实例 。
let exports;let compiledModule;WebAssembly.instantiateStreaming(fetch('sample.wasm')).then(obj => {exports = obj.instance.exports;//access compiled modulecompiledModule = obj.module;})导入对象实例化 WebAssembly 模块实例时 , 可以选择传递一个导入对象 , 该对象将包含要导入到新创建的模块实例中的值 。 它们可以是 4 种类型 。

  • 全局变量值
  • 函数
  • memory
  • table
导入对象可以视为提供给模块实例以帮助其完成任务的工具 。 如果未提供导入对象 , 则编译器将分配默认值 。
全局变量WebAssembly 允许你创建可从 JavaScript 和 WebAssembly 模块访问的全局变量实例 。 你可以导入 / 导出这些变量 , 并在一个或多个 WebAssembly 模块实例中使用它们 。
你可以使用 WebAssembly.Global() 构造器创建一个全局实例 。
const global = new WebAssembly.Global({value: 'i64',mutable: true}, 20);全局构造器接收两个参数 。
  • 一个对象 , 包含描述全局变量的数据类型和可变性的属性 。 允许的数据类型为 i32、i64、f32 或 f64
  • 实际变量的初始值 。 此值应为参数 1 中提到的类型 。 例如 , 如果你声明类型为 i32 , 则变量应为 32 位整数 。 同样 , 如果你声明类型为 f64 , 则变量应为 64 位浮点数 。
const global = new WebAssembly.Global({value: 'i64',mutable: true}, 20);let importObject = {js: {global}};WebAssembly.instantiateStreaming(fetch('global.wasm'), importObject)全局实例应传递到 importObject 上 , 以便在 WebAssembly 模块实例中访问它 。
Memory
在实例化时 , WebAssembly 模块将需要分配一个 memory 对象 。 该 memory 对象应与 importObject 一起传递 。 如果没能这样做 , 则 JIT 编译器将使用默认值自动创建一个 memory 对象并将其附加到实例 。
附加到模块实例的 memory 对象只是一个 ArrayBuffer 。 只需使用索引值 , 即可轻松访问 memory 。 此外 , 由于它是简单的 ArrayBuffer , 因此可以简单地在 JavaScript 和 WebAssembly 之间传递和共享值 。
Table
WebAssembly Table 是一个可调整大小的数组 , 位于 WebAssembly 的 memory 之外 。 该 Table 的值都是函数引用 。 尽管这听起来很像 WebAssembly memory , 但它们是不同的 , 主要区别在于 Memory 数组是原始字节 , 而 Table 数组是引用 。
引入 Table 主要是为了提高安全性 。
你可以使用 set()、grow() 和 get() 方法来操作 Table 。
演示
在这个演示中 , 我将使用 WebAssembly Studio 应用程序将一个 C 文件编译为.wasm 。 你可以在这里查看演示 。
我创建了一个函数来计算wasm 文件中一个数字的幂 。 我将必要的值传递给函数 , 并在JavaScript 中接收输出 。
同样 , 我在wasm 中进行了一些字符串操作 。 需要注意wasm 没有字符串类型 , 因此它用的是ASCII 值 。 返回到JavaScript 的值将指向存储输出的memory 位置 。 由于memory 对象是ArrayBuffer , 因此我要进行迭代 , 直到收到字符串中的所有字符为止 。
JavaScript 文件let exports;let buffer;(async() => {let response = await fetch('../out/main.wasm');let results = await WebAssembly.instantiate(await response.arrayBuffer());//or// let results = await WebAssembly.instantiateStreaming(fetch('../out/main.wasm'));let instance = results.instance;exports = instance.exports;buffer = new Uint8Array(exports.memory.buffer);findPower(5,3);printHelloWorld(); })();const findPower = (base = 0, power = 0) => {console.log(exports.power(base,power));}const printHelloWorld = () => {let pointer = exports.helloWorld();let str = "";for(let i = pointer;buffer[i];i++){str += String.fromCharCode(buffer[i]);}console.log(str);}C 文件#define WASM_EXPORT __attribute__((visibility("default")))#include WASM_EXPORTdouble power(double number,double power_value) {return pow(number,power_value);}WASM_EXPORTchar* helloWorld(){return "hello world";}用例WebAssembly 的诞生带来了很多全新的机遇 。