ค้นหาเว็บไซต์

javascript - ฉันจะส่ง ArrayBuffer จาก JS ไปยัง AssemblyScript/Wasm ได้อย่างไร?


ฉันมีโค้ด Typescript ที่ค่อนข้างตรงไปตรงมาที่ใช้วิเคราะห์รูปแบบข้อมูลเฉพาะ โดยอินพุตคือ UInt8Array ฉันได้ปรับแต่งมันจนสุดความสามารถแล้ว แต่ฉันคิดว่าตัวแยกวิเคราะห์ที่ค่อนข้างเรียบง่ายนี้น่าจะสามารถทำงานได้เร็วกว่าที่ฉันทำให้มันทำงานเป็น JS ฉันต้องการลองเขียนมันในเว็บแอสเซมบลีโดยใช้ AssemblyScript เพื่อให้แน่ใจว่าฉันไม่ได้เจอปัญหาประหลาด ๆ ของเครื่องมือ Javascript

เท่าที่ฉันเข้าใจตอนนี้ ฉันไม่สามารถส่ง TypedArray ไปยัง Wasm แล้วให้มันทำงานโดยอัตโนมัติได้ ตามที่ฉันเข้าใจ ฉันสามารถส่ง pointer ไปยัง array และควรสามารถเข้าถึงมันโดยตรงจาก Wasm โดยไม่ต้องคัดลอก array แต่ฉันไม่สามารถทำให้สิ่งนี้ทำงานกับ 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: memory access out of bounds" เมื่อฉันรันโค้ดนั้น

ปัญหาหลักคือข้อผิดพลาดที่ฉันได้รับกลับจาก Wasm นั้นไม่ได้ช่วยอะไรในการหาวิธีแก้ปัญหานี้ด้วยตนเองเลย สิ่งที่เห็นได้ชัดคือฉันพลาดบางแง่มุมสำคัญของวิธีที่สิ่งนี้ทำงานจริง ๆ เบื้องหลัง

ฉันจะส่ง TypedArray หรือ ArrayBuffer จาก JS ไปยัง Wasm โดยใช้ AssemblyScript จริง ๆ ได้อย่างไร?

คำตอบ #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 ของคุณ!

บทความที่เกี่ยวข้อง