Saat bekerja dengan WebAssembly, Anda sering kali ingin mendownload modul, mengompilasinya, membuat instance, dan kemudian menggunakan apa pun yang diekspornya dalam JavaScript. Postingan ini menjelaskan pendekatan yang kami rekomendasikan untuk efisiensi optimal.
Saat bekerja dengan WebAssembly, Anda sering kali ingin mengunduh modul, mengompilasinya, membuat instance, dan kemudian menggunakan apa pun yang diekspornya di JavaScript. Postingan ini dimulai dengan kode yang umum tetapi kurang optimal yang melakukan hal tersebut, membahas beberapa kemungkinan pengoptimalan, dan pada akhirnya menunjukkan cara paling sederhana dan efisien untuk menjalankan WebAssembly dari JavaScript.
Cuplikan kode ini melakukan dance download-compile-instanceiate yang 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 kita menggunakan new WebAssembly.Module(buffer)
untuk mengubah buffer respons menjadi modul. Ini adalah
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 bisa
gunakan await WebAssembly.compile(buffer)
sebagai gantinya:
(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 mencapainya dalam
kedua.
Hampir setiap operasi dalam cuplikan yang dimodifikasi sekarang asinkron, karena penggunaan await
membuat
jelas lagi. Satu-satunya pengecualian adalah new WebAssembly.Instance(module)
, yang memiliki buffer 4 KB yang sama
pembatasan ukuran di Chrome. Untuk konsistensi dan demi mempertahankan thread utama
gratis, kita bisa menggunakan model
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);
})();
Mari kembali ke pengoptimalan compile
yang saya petunjuk sebelumnya. Dengan streaming
kompilasi, browser sudah dapat
mulai mengompilasi modul WebAssembly selagi byte modul masih diunduh. Sejak download
dan kompilasi terjadi secara paralel, ini lebih cepat — terutama untuk payload besar.
Untuk mengaktifkan pengoptimalan ini, gunakan WebAssembly.compileStreaming
, bukan WebAssembly.compile
.
Perubahan ini juga memungkinkan kita menghapus buffer array perantara, karena sekarang kita bisa 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 menjadi Response
di instance Compute Engine. Jika response
tidak diperlukan di bagian lain dalam kode, Anda dapat meneruskan promise
ditampilkan oleh fetch
secara langsung, tanpa await
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);
})();
Namun, saya pribadi merasa lebih mudah membaca bila menulisnya di baris terpisah.
Lihat bagaimana kita mengompilasi respons ke dalam modul, lalu langsung membuat instance? Ternyata,
WebAssembly.instantiate
dapat mengompilasi dan membuat instance sekaligus. Tujuan
WebAssembly.instantiateStreaming
API melakukan hal ini dalam 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
,
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 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!