WebAssembly ile çalışırken genellikle bir modülü indirmek, derlemek, örneklemek ve ardından dışa aktardığı her şeyi JavaScript'te kullanmak istersiniz. Bu yayında, optimum verimlilik için önerdiğimiz yaklaşım açıklanmaktadır.
WebAssembly ile çalışırken genellikle bir modülü indirmek, derlemek, örneklemek ve ardından JavaScript'te dışa aktardığı her şeyi kullanmak istersiniz. Bu gönderi, tam olarak bunu yapan yaygın ancak en uygun kod snippet'iyle başlar, çeşitli olası optimizasyonları tartışır ve sonunda WebAssembly'i JavaScript'ten çalıştırmanın en basit ve en verimli yolunu gösterir.
Bu kod snippet'i, en uygun olmayan şekilde olsa da indirme-derleme-oluşturma işleminin tamamını 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);
})();
Bir yanıt arabelleğini modüle dönüştürmek için new WebAssembly.Module(buffer)
'ü nasıl kullandığımıza dikkat edin. Bu, senkronize bir API'dir. Yani işlemin tamamlanmasına kadar ana iş parçacığı engellenir. Chrome, kullanımını engellemek için 4 KB'tan büyük tamponlar için WebAssembly.Module
'ü 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)
, hâlâ en uygun yaklaşım değildir ancak bu konuya birazdan değineceğiz.
Değiştirilen snippet'teki neredeyse her işlem artık await
kullanımıyla açıkça anlaşılacağı üzere ayarsızdır. Bunun tek istisnası, Chrome'dakiyle aynı 4 KB arabellek boyutu kısıtlamasına sahip olan new WebAssembly.Instance(module)
'tir. Tutarlılık ve ana iş parçasını boş tutma için asenkron 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 değindiğim compile
optimizasyonuna dönelim. Akışlı derleme sayesinde tarayıcı, modül baytları indirilirken bile WebAssembly modülünü derlemeye başlayabilir. İndirme ve derleme işlemi paralel olarak gerçekleştiğinden bu yöntem, özellikle büyük yüklerde daha hızlıdır.
Bu optimizasyonu etkinleştirmek için WebAssembly.compile
yerine WebAssembly.compileStreaming
kullanın.
Bu değişiklik, await fetch(url)
tarafından döndürülen Response
örneğini doğrudan iletebildiğimiz için ara dizi arabelleğinden de kurtulmamızı sağlar.
(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 çözümlenen bir söz de kabul eder. Kodunuzun başka bir yerinde response
'ye ihtiyacınız yoksa sonucunu açıkça await
etmeden fetch
tarafından döndürülen vaadi doğrudan 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
sonucuna 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);
})();
Ancak şahsen ayrı bir satırda tutmayı daha okunaklı buluyorum.
Yanıtı nasıl bir modüle derleyip hemen örneklendirdiğimizi görüyor musunuz? WebAssembly.instantiate
, tek seferde derleyip örnekleyebiliyor. WebAssembly.instantiateStreaming
API'si bunu akış şeklinde 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 module
nesnesini tutmanın bir anlamı yoktur. Bu durumda kodu daha da basitleştirebilirsiniz:
// 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 kullanın
- WebAssembly modüllerini daha hızlı derlemek ve örneklemek için akış API'lerini kullanma
- İhtiyacınız olmayan kodlar yazmayın
WebAssembly ile eğlenin.