Saat menggunakan WebAssembly, Anda sering kali ingin mendownload modul, mengompilasi, membuat instance, lalu menggunakan apa pun yang diekspornya dalam JavaScript. Postingan ini menjelaskan pendekatan yang kami rekomendasikan untuk efisiensi optimal.
Saat menggunakan WebAssembly, sering kali Anda ingin mendownload modul, mengompilasi, membuat instance, lalu menggunakan apa pun yang diekspornya dalam JavaScript. Postingan ini dimulai dengan cuplikan kode umum tetapi kurang optimal yang melakukan hal tersebut, membahas beberapa kemungkinan pengoptimalan, dan akhirnya menunjukkan cara paling sederhana dan efisien dalam menjalankan WebAssembly dari JavaScript.
Cuplikan kode ini menjalankan tarian download-compile-instantiate lengkap, meskipun dengan cara yang kurang optimal:
Jangan menggunakannya!
(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 kita menggunakan new WebAssembly.Module(buffer)
untuk mengubah buffer respons menjadi modul. Ini adalah
API sinkron, artinya memblokir thread utama hingga selesai. Untuk mencegah penggunaannya, Chrome
menonaktifkan WebAssembly.Module
untuk buffer 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 segera membahasnya.
Hampir setiap operasi dalam cuplikan yang diubah kini bersifat asinkron, karena penggunaan await
memperjelas. Satu-satunya pengecualian adalah new WebAssembly.Instance(module)
, yang memiliki batasan ukuran buffer
4 KB yang sama di Chrome. Demi konsistensi dan demi menjaga 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 petunjukkan 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 untuk menghilangkan buffer 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 bagian lain dalam kode, Anda dapat meneruskan promise yang ditampilkan oleh fetch
secara langsung, tanpa melakukan await
pada hasilnya 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 Anda juga 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);
})();
Tapi saya pribadi merasa lebih mudah dibaca untuk memisahkannya dalam baris terpisah.
Lihat cara kita mengompilasi respons ke dalam modul, lalu langsung membuat instance? Ternyata,
WebAssembly.instantiate
dapat mengompilasi dan membuat instance dalam sekali jalan. WebAssembly.instantiateStreaming
API melakukan hal ini secara 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 kita terapkan dapat diringkas 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 Anda perlukan
Selamat bersenang-senang dengan WebAssembly!