ข้ามไปยังเนื้อหา

พื้นฐาน Memory

module WebAssembly สามารถประกาศ byte block ดิบที่เรียกว่า linear memory ซึ่งต่างจาก JavaScript heap โดย memory นี้เป็น flat, untyped array ของ byte ที่ทั้ง module Wasm และ JavaScript สามารถอ่านและเขียนได้โดยตรง เป็นกลไกหลักในการส่งข้อมูลที่ซับซ้อน — string, array, struct — ระหว่าง WebAssembly และ host

การประกาศ (memory N) จอง N หน้า ของ memory โดยแต่ละหน้ามีขนาดพอดี 64 KiB (65 536 byte) ขนาดขั้นต่ำที่มีประโยชน์คือหนึ่งหน้า:

(memory 1)

นี่จะให้ module ของคุณมี byte ที่ addressable ได้ 65 536 byte ทั้งหมดถูก initialise เป็น zero ตอน instantiation

WebAssembly มีคำสั่ง load และ store แบบ typed สำหรับอ่านและเขียนค่าที่ byte address เฉพาะเจาะจง

  • i32.store — pop address และ 32-bit integer จาก stack เขียน 4 byte ที่ address นั้น
  • i32.load — pop address จาก stack อ่าน 4 byte และ push ผลลัพธ์ i32

คำสั่งทั้งสองรองรับ attribute offset แบบ static ที่ถูกบวกเข้ากับ runtime address ซึ่งมีประโยชน์สำหรับการเข้าถึง field ภายใน struct

JavaScript ไม่สามารถเข้าถึง Wasm memory ได้จนกว่า module จะ export memory ออกมา ใช้ inline export syntax:

(memory (export "mem") 1)

ฝั่ง JavaScript ยังสามารถสร้าง WebAssembly.Memory object และส่งเป็น import ได้:

const memory = new WebAssembly.Memory({ initial: 1 });

นี่คือ module ที่สมบูรณ์ที่เก็บค่า i32 ที่ byte address 100 และโหลดกลับทันที:

(module
(memory (export "mem") 1)
(func (export "storeAndLoad") (param $val i32) (result i32)
i32.const 100 ;; address
local.get $val ;; value to store
i32.store
i32.const 100 ;; address
i32.load)) ;; load back

Runner ด้านล่างเก็บ 999 ที่ offset 100 อ่านกลับมา และอ่าน mem[0] ด้วย (ซึ่งควรเป็น 0 เนื่องจากไม่มีอะไรเขียนที่นั่น)

WebAssembly
ตัวเลือกBenefitCost
TypedArray view ทับ mem.buffer โดยตรงอ่าน/เขียน byte แบบ zero-copy รวดเร็ว ไม่ต้อง allocate เพิ่มต้องสร้าง view ใหม่ทุกครั้งที่ memory ถูก grow ไม่งั้น view เก่าจะชี้ไปยัง buffer ที่ detach ไปแล้ว
จัดการ layout ของ memory เอง (คำนวณ address ด้วยมือ)ควบคุม memory ได้เต็มที่ ไม่มี overhead จาก runtimeไม่มี garbage collector หรือ bounds-checking แบบ JavaScript คอยช่วย ต้องดูแล lifetime ของข้อมูลเอง
  • เขียนที่ address เกินขอบเขตของ page ที่จองไว้ (เช่น จอง (memory 1) แต่ store ที่ offset เกิน 65 536) ทำให้เกิด trap เพราะ WebAssembly ไม่มี garbage collector หรือ bounds-checking layer ให้แบบ JavaScript
  • ลืมว่า (memory N) ขยายได้ทีละทั้ง page (64 KiB) เท่านั้น จะจองแค่บางไบต์ไม่ได้ ทำให้เผลอ allocate เกินความจำเป็น
  • ลืม export memory ด้วย (memory (export "mem") 1) แล้วสงสัยว่าทำไม JavaScript เข้าถึง instance.exports.mem ไม่ได้

💡 ตัวอย่างจากของจริง

เครื่องมือแก้ไขภาพใน browser สไตล์ Squoosh ใช้ linear memory เก็บ raw pixel buffer โดยตรง แทนที่จะ copy ข้อมูลภาพไปมาระหว่าง JavaScript กับ Wasm ทุกครั้งที่ประมวลผล ทำให้ resize หรือ compress ภาพขนาดใหญ่ได้เร็วขึ้นมาก

หนึ่งหน้าของ WebAssembly memory มีกี่ bytes?
คำสั่งใดที่ใช้เขียน integer 4 bytes ลง linear memory?
ค่าเริ่มต้นของ bytes ทุกตัวใน WebAssembly memory ที่สร้างใหม่คือเท่าใด?