У меня есть довольно простой фрагмент кода Typescript, который анализирует определенный формат данных, ввод — это UInt8Array. Я максимально оптимизировал его, но я думаю, что этот довольно простой анализатор должен работать быстрее, чем я могу заставить его работать как JS. Я хотел попробовать написать его в веб-сборке с использованием AssemblyScript, чтобы убедиться, что я не сталкиваюсь с какими-либо причудами движков Javascript.
Как я понял, я не могу просто передать TypedArray в Wasm и заставить его работать автоматически. Насколько я понимаю, я могу передать указатель на массив и иметь доступ к нему напрямую из Wasm без копирования массива. Но я не могу заставить это работать с AssemblyScript.
Ниже приведен минимальный пример, показывающий, как мне не удается передать ArrayBuffer в Wasm.
Код для настройки экспорта 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
});
Следующий код вызывает 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);
И AssemblyScript, скомпилированный в WASM, выглядит следующим образом:
import "allocator/arena";
export function parse(offset: usize): number {
return load<u8>(offset);
}
Я получаю «RuntimeError: доступ к памяти вне границ», когда я выполняю этот код.
Основная проблема заключается в том, что ошибки, которые я получаю от Wasm, просто не помогают мне разобраться в этом самостоятельно. Я, очевидно, упускаю некоторые важные аспекты того, как это на самом деле работает за кулисами.
Как на самом деле передать TypedArray или ArrayBuffer из JS в Wasm с помощью AssemblyScript?
@guest271314guest271314 ArrayBuffer на самом деле не передается в WASM, как я изначально думал. Но каждый раз, когда я пытался получить доступ к такому свойству, как длина массива, я выдавал ошибку. Насколько я понимаю, в WASM передается только указатель на расположение ArrayBuffer в памяти. Но я, вероятно, ошибаюсь в этом, хотя я почти уверен, что прав в том, что настоящий ArrayBuffer не передается.
Что parse ожидает в качестве аргумента?
@ guest271314 адрес в памяти, который я могу использовать для выполнения команды загрузки. В реальном коде я бы перебирал массив, поэтому я бы также передал длину. Я попытался обработать параметр как TypedArray, но это не сработало. Насколько я читал, если я передаю ArrayBuffer, я получаю указатель на массив на стороне WASM.
Я не знаком с AssemblyScript, но имею большой опыт работы с C/C++ для WASM. На любом языке, который вы используете, вы не можете передать объект массива, но скопируйте его в область кучи WAS. Это связано с тем, что WASM строго запрещено трогать какую-либо память за пределами кучи WASM, и WASM не может понимать только массив как тип, а только числовые типы. В WASM для C вы должны вызвать malloc того же размера ArrayBuffer, передать указатель на JS, а затем преобразовать его в Uint8Array и скопировать ArrayBuffer в Uint8Array с помощью метода Uinit8Array.set(). Другого пути действительно нет. Надеюсь, это поможет.
вероятно, связано с Эта проблема



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В AssemblyScript существует множество способов чтения данных из памяти. Самый быстрый и быстрый способ получить эти данные — использовать связанную функцию в импорте функций вашего модуля, чтобы вернуть указатель на сами данные.
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);
Затем в 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);
};
Однако есть гораздо более простой способ получения данных, включающий возврат ссылки из AssemblyScript и последующее использование загрузчика AssemblyScript.
let myData = new Float64Array(100); // have some data in AssemblyScript
export function getData(): Float64Array {
return myData;
}
Затем в JavaScript мы можем использовать загрузчик ASUtil, предоставляемый AssemblyScript.
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);
Я настоятельно рекомендую использовать второй пример для ясности кода, если производительность не является абсолютно критической.
Удачи с вашим проектом на AssemblyScript!
Возможно, глупый вопрос, но откуда должен взяться «getArray» (или как он должен выглядеть, поскольку кажется, что он определен в нашем модуле AS)?
Спасибо! Это действительно то, что мне было нужно (и я обнаружил)!
Ожидается ли, что
offsetбудет собственностьюUnit8Array? Вы пытаетесь получитьbyteOffset?parseожидает, что параметр будетUnit8Arrayили целым числом?