简体   繁体   English

如何将ArrayBuffer从JS传递给AssemblyScript / Wasm?

[英]How can I pass an ArrayBuffer from JS to AssemblyScript/Wasm?

I have a pretty straightforward piece of Typescript code that parses a specific data format, the input is a UInt8Array. 我有一个非常简单的Typescript代码解析特定的数据格式,输入是一个UInt8Array。 I've optimized it as far as I can, but I think this rather simple parser should be able to run faster than I can make it run as JS. 我已经尽可能优化了它,但我认为这个相当简单的解析器应该能够比我以JS运行的速度运行得更快。 I wanted to try out writing it in web assembly using AssemblyScript to make sure I'm not running into any quirks of the Javascript engines. 我想尝试使用AssemblyScript在Web程序集中编写它,以确保我没有遇到任何Javascript引擎的怪癖。

As I figured out now, I can't just pass a TypedArray to Wasm and have it work automatically. 正如我现在想的那样,我不能只将TypedArray传递给Wasm并让它自动运行。 As far as I understand, I can pass a pointer to the array and should be able to access this directly from Wasm without copying the array. 据我所知,我可以传递指向数组的指针,并且应该能够直接从Wasm访问它而无需复制数组。 But I can't get this to work with AssemblyScript. 但我无法使用AssemblyScript。

The following is a minimal example that shows how I'm failing to pass an ArrayBuffer to Wasm. 以下是一个最小的例子,展示了我如何将ArrayBuffer传递给Wasm。

The code to set up the Wasm export is mostly from the automatically generated boilerplate: 设置Wasm导出的代码主要来自自动生成的样板:

const fs = require("fs");
const compiled = new WebAssembly.Module(
  fs.readFileSync(__dirname + "/build/optimized.wasm")
);
const imports = {
  env: {
    abort(msgPtr, filePtr, line, column) {
      throw new Error(`index.ts: abort at [${line}:${column}]`);
    }
  }
};
Object.defineProperty(module, "exports", {
  get: () => new WebAssembly.Instance(compiled, imports).exports
});

The following code invokes the WASM, index.js is the glue code above. 以下代码调用WASM,index.js是上面的粘合代码。

const m = require("./index.js");
const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
const result = m.parse(data.buffer);

And the AssemblyScript that is compiled to WASM is the following: 编译为WASM的AssemblyScript如下:

import "allocator/arena";

export function parse(offset: usize): number {
  return load<u8>(offset);
}

I get a "RuntimeError: memory access out of bounds" when I execute that code. 当我执行该代码时,我得到一个“RuntimeError:内存访问超出范围”。

The major problem is that the errors I get back from Wasm are simply not helpful to figure this out on my own. 主要的问题是我从Wasm那里得到的错误对我自己解决这个问题根本没有帮助。 I'm obviously missing some major aspects of how this actually works behind the scenes. 我显然错过了一些在幕后真正起作用的主要方面。

How do I actually pass a TypedArray or an ArrayBuffer from JS to Wasm using AssemblyScript? 如何使用AssemblyScript将TypedArray或ArrayBuffer从JS传递给Wasm?

In AssemblyScript, there are many ways to read data from the memory. 在AssemblyScript中,有许多方法可以从内存中读取数据。 The quickest and fastest way to get this data is to use a linked function in your module's function imports to return a pointer to the data itself. 获取此数据的最快速最快的方法是在模块的函数import中使用链接函数来返回指向数据本身的指针。

let myData = new Float64Array(100); // have some data in AssemblyScript

// We should specify the location of our linked function
@external("env", "sendFloat64Array")
declare function sendFloat64Array(pointer: usize, length: i32): void;

/**
 * The underlying array buffer has a special property called `data` which
 * points to the start of the memory.
 */
sendFloat64Data(myData.buffer.data, myData.length);

Then in JavaScript, we can use the Float64Array constructor inside our linked function to return the values directly. 然后在JavaScript中,我们可以使用链接函数中的Float64Array构造函数直接返回值。

/**
 * This is the fastest way to receive the data. Add a linked function like this.
 */
imports.env.sendFloat64Array = function sendFloat64Array(pointer, length) {
  var data = new Float64Array(wasmmodule.memory.buffer, pointer, length);
};

However, there is a much clearer way to obtain the data, and it involves returning a reference from AssemblyScript, and then using the AssemblyScript loader. 但是,有一种更清晰的方法来获取数据,它涉及从AssemblyScript返回引用,然后使用AssemblyScript加载器。

let myData = new Float64Array(100); // have some data in AssemblyScript

export function getData(): Float64Array {
  return myData;
}

Then in JavaScript, we can use the ASUtil loader provided by AssemblyScript. 然后在JavaScript中,我们可以使用AssemblyScript提供的ASUtil加载器。

import { instantiateStreaming } from "assemblyscript/lib/loader";

let wasm: ASUtil = await instantiateStreaming(fetch("myAssemblyScriptModule.wasm"), imports);

let dataReference: number = wasm.getData();
let data: Float64Array = wasm.getArray(Float64Array, dataReference);

I highly recommend using the second example for code clarity reasons, unless performance is absolutely critical. 出于代码清晰度的原因,我强烈建议使用第二个示例,除非性能绝对至关重要。

Good luck with your AssemblyScript project! 祝你的AssemblyScript项目好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM