เมื่อทำงานกับ WebAssembly คุณมักต้องการดาวน์โหลดโมดูล คอมไพล์ สร้างอินสแตนซ์ จากนั้นใช้สิ่งใดก็ตามที่ส่งออกไปใน JavaScript โพสต์นี้อธิบายวิธีที่เราแนะนำเพื่อประสิทธิภาพสูงสุด
เมื่อทำงานกับ WebAssembly คุณมักต้องการดาวน์โหลดโมดูล คอมไพล์ สร้างอินสแตนซ์ และ จากนั้นใช้อะไรก็ตามที่ส่งออกเป็น JavaScript โพสต์นี้เริ่มต้นด้วยโค้ดที่ใช้กันโดยทั่วไปแต่มีประสิทธิภาพต่ำกว่ามาตรฐาน กล่าวถึงการเพิ่มประสิทธิภาพที่เป็นไปได้หลายๆ แบบ และสุดท้ายจะแสดง วิธีที่ง่ายและมีประสิทธิภาพที่สุดในการเรียกใช้ WebAssembly จาก JavaScript
ข้อมูลโค้ดนี้ทำให้การดาวน์โหลด-คอมไพล์ ไม่หยุดนิ่ง แม้ว่าจะเป็นการกระทำที่ไม่เหมาะสมก็ตาม:
ห้ามใช้
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = new WebAssembly.Module(buffer);
const instance = new WebAssembly.Instance(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
อย่าลืมสังเกตวิธีที่เราใช้ new WebAssembly.Module(buffer)
เพื่อเปลี่ยนบัฟเฟอร์การตอบกลับให้เป็นโมดูล นี่คือ
API แบบซิงโครนัส ซึ่งหมายความว่า API จะบล็อกเทรดหลักจนกว่าจะเสร็จสมบูรณ์ เพื่อลดการใช้งาน Chrome
ปิดใช้ WebAssembly.Module
สำหรับบัฟเฟอร์ที่มีขนาดใหญ่กว่า 4 KB ในการแก้ปัญหาขนาดสูงสุด
เราสามารถ
ใช้ await WebAssembly.compile(buffer)
แทน:
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
const instance = new WebAssembly.Instance(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
await WebAssembly.compile(buffer)
ยังยังไม่ใช่แนวทางที่ดีที่สุด แต่เราจะพูดถึงเรื่องนี้
อย่างที่สอง
ปัจจุบันการดำเนินการเกือบทุกอย่างในข้อมูลโค้ดที่แก้ไขจะเป็นแบบไม่พร้อมกัน เนื่องจากการใช้ await
ล้าง ข้อยกเว้นเพียงอย่างเดียวคือ new WebAssembly.Instance(module)
ซึ่งมีบัฟเฟอร์ 4 KB เท่ากัน
การจำกัดขนาดใน Chrome เพื่อความสอดคล้องและเพื่อเก็บเทรดหลักไว้
ฟรี เราก็สามารถใช้ฟังก์ชัน
แบบอะซิงโครนัส
WebAssembly.instantiate(module)
(async () => {
const response = await fetch('fibonacci.wasm');
const buffer = await response.arrayBuffer();
const module = await WebAssembly.compile(buffer);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
กลับมาที่การเพิ่มประสิทธิภาพ compile
ที่ฉันบอกใบ้ก่อนหน้านี้กัน มีสตรีมมิง
คอมไพล์ เบราว์เซอร์สามารถ
ให้เริ่มคอมไพล์โมดูล WebAssembly ในขณะที่ไบต์ของโมดูลยังดาวน์โหลดอยู่ ตั้งแต่ดาวน์โหลด
และการคอมไพล์จะดำเนินการควบคู่กันไปด้วย การดำเนินการนี้จะเร็วกว่า โดยเฉพาะสำหรับเพย์โหลดขนาดใหญ่
หากต้องการเปิดใช้การเพิ่มประสิทธิภาพนี้ ให้ใช้ WebAssembly.compileStreaming
แทน WebAssembly.compile
การเปลี่ยนแปลงนี้ยังช่วยให้เราสามารถกำจัดบัฟเฟอร์อาร์เรย์ขั้นกลาง เนื่องจากขณะนี้เราสามารถส่งผ่าน
await fetch(url)
ส่งกลับอินสแตนซ์ Response
โดยตรง
(async () => {
const response = await fetch('fibonacci.wasm');
const module = await WebAssembly.compileStreaming(response);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
WebAssembly.compileStreaming
API ยังยอมรับคำมั่นสัญญาที่แก้ไขให้เป็น Response
อินสแตนซ์ หากคุณไม่ใช้ response
ในส่วนอื่นในโค้ด ก็ส่งคำสัญญาได้
ส่งคืนโดย fetch
โดยตรง โดยไม่await
แสดงผลลัพธ์อย่างชัดเจน:
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const module = await WebAssembly.compileStreaming(fetchPromise);
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
หากไม่ต้องการผลลัพธ์ fetch
จากที่อื่น คุณก็ส่งไปโดยตรงได้เช่นกัน
(async () => {
const module = await WebAssembly.compileStreaming(
fetch('fibonacci.wasm'));
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
แต่โดยส่วนตัวแล้ว ฉันพบว่าการแบ่งออกเป็นอีกบรรทัดหนึ่งอ่านได้ง่ายกว่า
ดูวิธีที่เรารวบรวมคำตอบลงในโมดูล แล้วสร้างตัวอย่างทันที ผลที่ได้คือ
WebAssembly.instantiate
คอมไพล์และสร้างอินสแตนซ์ได้ในครั้งเดียว
WebAssembly.instantiateStreaming
API จะดำเนินการในลักษณะของการสตรีม:
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const { module, instance } = await WebAssembly.instantiateStreaming(fetchPromise);
// To create a new instance later:
const otherInstance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
หากคุณต้องการเพียงอินสแตนซ์เดียว ก็ไม่มีประโยชน์ที่จะเก็บออบเจ็กต์ module
ไว้
การลดความซับซ้อนของโค้ดเพิ่มเติม
// This is our recommended way of loading WebAssembly.
(async () => {
const fetchPromise = fetch('fibonacci.wasm');
const { instance } = await WebAssembly.instantiateStreaming(fetchPromise);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
การเพิ่มประสิทธิภาพที่เรานำไปใช้สามารถสรุปได้ดังนี้
- ใช้ API แบบอะซิงโครนัสเพื่อหลีกเลี่ยงการบล็อกเทรดหลัก
- ใช้ API การสตรีมเพื่อคอมไพล์และสร้างอินสแตนซ์โมดูล WebAssembly ได้เร็วขึ้น
- อย่าเขียนโค้ดที่ไม่จำเป็น
ขอให้สนุกกับ WebAssembly