Как передать ArrayBuffer из JS в AssemblyScript/Wasm?

У меня есть довольно простой фрагмент кода 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?

Ожидается ли, что offset будет собственностью Unit8Array? Вы пытаетесь получить byteOffset? parse ожидает, что параметр будет Unit8Array или целым числом?

guest271314 17.02.2019 23:25

@guest271314guest271314 ArrayBuffer на самом деле не передается в WASM, как я изначально думал. Но каждый раз, когда я пытался получить доступ к такому свойству, как длина массива, я выдавал ошибку. Насколько я понимаю, в WASM передается только указатель на расположение ArrayBuffer в памяти. Но я, вероятно, ошибаюсь в этом, хотя я почти уверен, что прав в том, что настоящий ArrayBuffer не передается.

Mad Scientist 17.02.2019 23:27

Что parse ожидает в качестве аргумента?

guest271314 17.02.2019 23:28

@ guest271314 адрес в памяти, который я могу использовать для выполнения команды загрузки. В реальном коде я бы перебирал массив, поэтому я бы также передал длину. Я попытался обработать параметр как TypedArray, но это не сработало. Насколько я читал, если я передаю ArrayBuffer, я получаю указатель на массив на стороне WASM.

Mad Scientist 17.02.2019 23:30

Я не знаком с AssemblyScript, но имею большой опыт работы с C/C++ для WASM. На любом языке, который вы используете, вы не можете передать объект массива, но скопируйте его в область кучи WAS. Это связано с тем, что WASM строго запрещено трогать какую-либо память за пределами кучи WASM, и WASM не может понимать только массив как тип, а только числовые типы. В WASM для C вы должны вызвать malloc того же размера ArrayBuffer, передать указатель на JS, а затем преобразовать его в Uint8Array и скопировать ArrayBuffer в Uint8Array с помощью метода Uinit8Array.set(). Другого пути действительно нет. Надеюсь, это поможет.

Bumsik Kim 17.02.2019 23:51

вероятно, связано с Эта проблема

bigless 18.02.2019 03:29
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
8
6
3 386
1

Ответы 1

В 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)?

A_P 16.02.2020 23:48

Спасибо! Это действительно то, что мне было нужно (и я обнаружил)!

A_P 18.02.2020 20:47

Другие вопросы по теме