WebAssembly ile çalışırken, genellikle bir modülü indirmek, derlemek, örneklendirmek ve ardından JavaScript'te dışa aktardığı her şeyi kullanmak istersiniz. Bu gönderide, optimum verimlilik için önerdiğimiz yaklaşım açıklanmaktadır.
WebAssembly ile çalışırken genellikle bir modülü indirmek, derlemek, örneklendirmek ve JavaScript'te dışa aktardıkları her şeyi kullanmak istersiniz. Bu yayın, tam olarak bunu yapan yaygın ancak uygun olmayan bir kod snippet'iyle başlıyor, olası birkaç optimizasyondan bahsediyor ve sonunda WebAssembly'yi JavaScript'ten çalıştırmanın en basit, en verimli yolunu gösteriyor.
Bu kod snippet'i, indirme-derleme ve başlatma dansını en uygun şekilde olmasa da eksiksiz bir şekilde gerçekleştirir:
Bunu kullanmayın!
(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);
})();
Yanıt arabelleğini modüle dönüştürmek için new WebAssembly.Module(buffer)
öğesini nasıl kullandığımıza dikkat edin. Bu eşzamanlı bir API'dir. Yani tamamlanana kadar ana iş parçacığını engeller. Chrome, kullanılmasını önlemek için 4 KB'tan büyük arabelleklerde WebAssembly.Module
öğesini devre dışı bırakır. Boyut sınırını aşmak için bunun yerine await WebAssembly.compile(buffer)
kullanabiliriz:
(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)
, hala ideal yaklaşım değildir, ancak buna birazdan değineceğiz.
await
kullanımının anlaşılması açıkça anlaşıldığı için, değiştirilen snippet'teki neredeyse her işlem artık eşzamansızdır. Bunun tek istisnası, Chrome'daki aynı 4 KB arabellek boyutu kısıtlamasına sahip olan new WebAssembly.Instance(module)
'tir. Tutarlılık için ve ana iş parçacığını serbest tutmak amacıyla eşzamansız WebAssembly.instantiate(module)
kullanabiliriz.
(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);
})();
Daha önce bahsettiğim compile
optimizasyonuna dönelim. Akış derlemesi sayesinde tarayıcı, modül baytları indirilmeye devam ederken WebAssembly modülünü derlemeye başlayabilir. İndirme ve derleme işlemleri birbirine paralel olarak gerçekleştiğinden, özellikle büyük boyutlu dosyalarda bu işlem daha hızlıdır.
Bu optimizasyonu etkinleştirmek için WebAssembly.compile
yerine WebAssembly.compileStreaming
kullanın.
Artık await fetch(url)
tarafından döndürülen Response
örneğini doğrudan geçirebildiğimiz için bu değişiklik sayesinde ara dizi arabelleğinden de kurtulabiliriz.
(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
örneğine dönüşen bir taahhüdü de kabul eder. Kodunuzda başka bir yerde response
için ihtiyacınız yoksa sonucunu açıkça await
belirtmeden doğrudan fetch
tarafından döndürülen sözü iletebilirsiniz:
(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
sonucunu başka bir yerde de ihtiyacınız yoksa doğrudan iletebilirsiniz:
(async () => {
const module = await WebAssembly.compileStreaming(
fetch('fibonacci.wasm'));
const instance = await WebAssembly.instantiate(module);
const result = instance.exports.fibonacci(42);
console.log(result);
})();
Yine de bunu ayrı bir satırda saklamanın daha okunaklı olduğunu düşünüyorum.
Yanıtı nasıl bir modül halinde derlediğimizi ve sonra nasıl hemen örneklediğimizi görüyor musunuz? WebAssembly.instantiate
, tek seferde derleyip örnek oluşturabilir. WebAssembly.instantiateStreaming
API, bunu akış yöntemiyle yapar:
(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);
})();
Yalnızca tek bir örneğe ihtiyacınız varsa kodu daha da basitleştirerek module
nesnesini tutmanın bir anlamı yoktur:
// 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);
})();
Uyguladığımız optimizasyonlar şu şekilde özetlenebilir:
- Ana iş parçacığının engellenmesini önlemek için eşzamansız API'ler kullanma
- WebAssembly modüllerini daha hızlı derlemek ve örneklendirmek için akış API'lerini kullanın
- İhtiyacınız olmayan kodu yazmayın
WebAssembly ile iyi eğlenceler!