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 ของคุณ!
