Saat menggunakan WebAssembly, Anda sering kali ingin mendownload modul, mengompilasi, membuat instance, lalu menggunakan apa pun yang diekspornya di JavaScript. Postingan ini menjelaskan pendekatan yang direkomendasikan untuk efisiensi yang optimal.
Saat menggunakan WebAssembly, Anda sering kali ingin mendownload modul, mengompilasi, membuat instance, lalu menggunakan apa pun yang diekspornya dalam JavaScript. Postingan ini dimulai dengan cuplikan kode umum, tetapi tidak optimal, yang melakukan hal tersebut, membahas beberapa kemungkinan pengoptimalan, dan pada akhirnya menunjukkan cara paling sederhana dan paling efisien untuk menjalankan WebAssembly dari JavaScript.
Cuplikan kode ini melakukan download-kompilasi-pembuatan instance lengkap, meskipun dengan cara yang kurang optimal:
Jangan gunakan ini!
(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);
})();
Perhatikan cara kami menggunakan new WebAssembly.Module(buffer)
untuk mengubah buffering respons menjadi modul. Ini adalah
API sinkron, yang berarti API ini memblokir thread utama hingga selesai. Untuk mencegah penggunaannya, Chrome
menonaktifkan WebAssembly.Module
untuk buffering yang lebih besar dari 4 KB. Untuk mengatasi batas ukuran, kita dapat
menggunakan 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)
masih bukan pendekatan yang optimal, tetapi kita akan membahasnya dalam
beberapa detik.
Hampir setiap operasi dalam cuplikan yang diubah kini bersifat asinkron, seperti yang dijelaskan oleh penggunaan await
. Satu-satunya pengecualian adalah new WebAssembly.Instance(module)
, yang memiliki batasan ukuran buffer
4 KB yang sama di Chrome. Untuk konsistensi dan agar thread utama
tetap bebas, kita dapat menggunakan
WebAssembly.instantiate(module)
asinkron.
(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);
})();
Mari kita kembali ke pengoptimalan compile
yang saya singgung sebelumnya. Dengan kompilasi streaming, browser sudah dapat mulai mengompilasi modul WebAssembly saat byte modul masih didownload. Karena download
dan kompilasi terjadi secara paralel, proses ini lebih cepat — terutama untuk payload besar.
Untuk mengaktifkan pengoptimalan ini, gunakan WebAssembly.compileStreaming
, bukan WebAssembly.compile
.
Perubahan ini juga memungkinkan kita menghapus buffering array perantara, karena sekarang kita dapat meneruskan
instance Response
yang ditampilkan oleh await fetch(url)
secara langsung.
(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 juga menerima promise yang di-resolve ke instance
Response
. Jika tidak memerlukan response
di tempat lain dalam kode, Anda dapat meneruskan promise
yang ditampilkan oleh fetch
secara langsung, tanpa await
hasil secara eksplisit:
(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);
})();
Jika tidak memerlukan hasil fetch
di tempat lain, Anda bahkan dapat meneruskannya secara langsung:
(async () => {
const module = await WebAssembly.compileStreaming(
fetch('fibonacci.wasm'));
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
Namun, menurut saya pribadi, lebih mudah dibaca jika tetap berada di baris terpisah.
Lihat cara kita mengompilasi respons ke dalam modul, lalu langsung membuat instance-nya? Ternyata,
WebAssembly.instantiate
dapat mengompilasi dan membuat instance sekaligus. WebAssembly.instantiateStreaming
API melakukannya dengan cara streaming:
(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);
})();
Jika Anda hanya memerlukan satu instance, tidak ada gunanya menyimpan objek module
,
sehingga menyederhanakan kode lebih lanjut:
// 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);
})();
Pengoptimalan yang kami terapkan dapat dirangkum sebagai berikut:
- Menggunakan API asinkron untuk menghindari pemblokiran thread utama
- Menggunakan API streaming untuk mengompilasi dan membuat instance modul WebAssembly dengan lebih cepat
- Jangan menulis kode yang tidak diperlukan
Selamat bersenang-senang dengan WebAssembly.