WebAssembly
JavaScript ile birlikte kullanılmak üzere tasarlanan WebAssembly (Wasm), JavaScript'ten çok daha yüksek ve daha tutarlı bir yürütme hızına sahip olabilir - C, C++ veya Rust'a benzer. Deno WebAssembly modüllerini, tarayıcıların sağladığı aynı arayüzlerle çalıştırabilir ve bunları modül olarak içe aktarabilir.
Wasm modülleri
Deno 2.1 ile birlikte, WebAssembly modülleri içe aktarılabilir ve kullanımının türü kontrol edilir.
Deno, Wasm modüllerinin dışa aktarımlarını anlayıp tür kontrolünden geçirebiliyor. Bu, daha güvenli bir geliştirme deneyimi sağlar.
Diyelim ki iki sayıyı toplayan ve sonucu döndüren bir add
fonksiyonu içeren
WebAssembly metin formatı dosyamız var:
(module
(func (export "add") (param $a i32) (param $b i32) (result i32)
local.get $a
local.get $b
i32.add
)
)
Bunu add.wasm
'a derlemek için
wat2wasm aracını kullanabiliriz:
wat2wasm add.wat
Sonra bu WebAssembly modülünü bir içe aktarma ifadesiyle kullanabiliriz:
import { add } from "./add.wasm";
console.log(add(1, 2));
> deno run main.ts
3
Tür Kontrolü
Deno, Wasm modüllerinin dışa aktarımlarını anlayabilmekte ve bunların kullanımını tür kontrolünden geçirmektedir. Önceki örnekte add
fonksiyonunu yanlış çağırırsak bir tür kontrol hatası göreceğiz.
import { add } from "./add.wasm";
console.log(add(1, ""));
> deno check main.ts
Check file:///.../main.ts
error: TS2345 [ERROR]: Argument of type 'string' is not assignable to parameter of type 'number'.
console.log(add(1, ""));
~~
at file:///.../main.ts:3:20
İçe Aktarmalar
JavaScript gibi, Wasm modülleri de başka modülleri içe aktarabilir.
Örneğin, "./values.js"
belirtisini içe aktaran ve getValue
dışa aktarmasını çağıran bir Wasm modülü yaratabiliriz:
(module
(import "./time.ts" "getTimeInSeconds" (func $get_time (result i32)))
(func (export "getValue") (result i32)
call $get_time
)
)
export function getTimeInSeconds() {
return Date.now() / 1000;
}
import { getValue } from "./toolkit.wasm";
console.log(getValue());
Şimdi çalıştırdığımızda:
> wat2wasm toolkit.wat
> deno run main.ts
1732147633
V:\scratch
> deno run main.ts
1732147637
İçe Aktarma belirticilerini geçersiz kılma
Sıklıkla Wasm modülleri, başka bir JavaScript modülünü içe aktarmayı kolaylaştırmak için göreli bir belirtici kullanmazlar. Diyelim ki önceki benzer kurulumda, ancak Wasm modülü "env" belirticisi aracılığıyla içe aktarım yapıyor.
(module
(import "env" "get_time_in_seconds" (func $get_time (result i32)))
(func (export "getValue") (result i32)
call $get_time
)
)
function getTimeInSeconds() {
return Date.now() / 1000;
}
export { getTimeInSeconds as get_time_in_seconds };
import { getValue } from "./toolkit.wasm";
console.log(getValue());
> wat2wasm toolkit.wat
> deno run main.ts
error: Relative import path "env" not prefixed with / or ./ or ../
at file:///.../toolkit.wasm
Bu hata, göreli bir içe aktarma belirticisi kullanılmadığında meydana gelir. Belirticiyi deno.json dosyası üzerinden bir içe aktarma haritası ile eşleştirmek gereklidir.
Neyse ki, bunu çalışır hale getirmek oldukça basit; belirticiyi deno.json dosyası üzerinden bir içe aktarma haritası ile eşleştirebiliriz:
{
"imports": {
"env": "./env.ts"
}
}
Artık çalışıyor:
> deno run main.ts
1732148355
WebAssembly API aracılığıyla WebAssembly kullanma
Deno'da WebAssembly'i çalıştırmak için tek gereken, çalıştırılacak bir Wasm modülüdür. Aşağıdaki modül, çağrıldığında sadece 42
döndüren bir main
fonksiyonu Dışa Aktarmaktadır:
// deno-fmt-ignore
const wasmCode = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127,
3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0,
5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145,
128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97,
105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0,
65, 42, 11
]);
const wasmModule = new WebAssembly.Module(wasmCode);
const wasmInstance = new WebAssembly.Instance(wasmModule);
const main = wasmInstance.exports.main as CallableFunction;
console.log(main().toString());
WebAssembly'i WebAssembly API aracılığıyla yüklemek için aşağıdaki adımların gerçekleştirilmesi gerekir:
- İkili veriyi almak (genellikle
.wasm
dosyası biçiminde, ancak şu anda basit bir byte dizisi kullanıyoruz) - İkili veriyi
WebAssembly.Module
nesnesine derlemek - WebAssembly modülünü başlatmak
WebAssembly, insan tarafından okunabilecek veya elle yazılabilecek bir format değildir; ikili veridir. .wasm
dosyalarınız, Rust, Go veya AssemblyScript gibi bir dil için bir derleyici tarafından üretilmelidir.
Örneğin, yukarıda bahsedilen baytlara derlenen bir Rust programı aşağıdaki gibi görünür:
#[no_mangle]
pub fn main() -> u32 { // u32, 32 bit hafıza kullanan bir işaretsiz tamsayıyı temsil eder.
42
}
Streaming WebAssembly API'lerini Kullanma
WebAssembly modülünü almak, derlemek ve başlatmanın en verimli
yolu, WebAssembly API'sinin streaming varyantlarını kullanmaktır. Örneğin, instantiateStreaming
'i fetch
ile birleştirerek tüm üç adımı tek seferde gerçekleştirebilirsiniz:
const { instance, module } = await WebAssembly.instantiateStreaming(
fetch("https://wpt.live/wasm/incrementer.wasm"),
);
const increment = instance.exports.increment as (input: number) => number;
console.log(increment(41));
.wasm
dosyasının application/wasm
MIME türü ile sunulması gerektiğini unutmayın. Modül oluşturulmadan önce modül üzerinde ek çalışma yapmak isterseniz bunun yerine compileStreaming
yöntemini kullanabilirsiniz:
const module = await WebAssembly.compileStreaming(
fetch("https://wpt.live/wasm/incrementer.wasm"),
);
/* biraz daha iş yapın */
const instance = await WebAssembly.instantiate(module);
instance.exports.increment as (input: number) => number;
Herhangi bir nedenle streaming yöntemlerini kullanma şansınız yoksa daha az verimli compile
ve instantiate
yöntemlerine geçiş yapabilirsiniz.
Streaming yöntemlerinin daha verimli hale gelmesini sağlayan derinlemesine araştırma için bu gönderiye göz atabilirsiniz.
WebAssembly API
WebAssembly API'sinin tüm parçaları hakkında daha fazla bilgi Deno Referans Kılavuzu
ve MDN üzerinde bulunabilir.
Sayısal Olmayan Türlerle Çalışma
Bu belgedeki kod örnekleri yalnızca sayısal türler kullanılmıştır. Daha karmaşık türlerle (örneğin, dizeler veya sınıflar) WebAssembly çalıştırmak için JavaScript ile WebAssembly'ye derlenen diller arasında tür bağlamaları oluşturan araçlar kullanmanız gerekecektir.
JavaScript ve Rust arasında tür bağlamaları oluşturmayı, bunu bir ikili dosyaya derlemeyi ve bir JavaScript programından çağırmayı gösteren bir örnek MDN üzerinde bulabilirsiniz.
Rust+WebAssembly ile Web API'leriyle çok fazla çalışma yapmayı planlıyorsanız, web_sys ve js_sys Rust kütüphanelerini yararlı bulabilirsiniz. web_sys
, Deno'da mevcut olan çoğu Web API'sine bağlamaları içerirken, js_sys
JavaScript'in standart ve yerleşik nesnelerine bağlamalar sağlar.
Optimize Etme
Üretim yapımları için WebAssembly ikili dosyaları üzerinde optimizasyonlar gerçekleştirebilirsiniz. Eğer ikili dosyaları bir ağ üzerinden sunuyorsanız, boyut için optimize etme gerçekten fark yaratabilir. Eğer esas olarak WebAssembly'yi bir sunucuda hesaplama açısından yoğun görevleri yürütmek için çalıştırıyorsanız, hız için optimize etme faydalı olabilir. Üretim oluşturma işlemlerini optimize etme konusunda iyi bir kılavuzu buradan bulabilirsiniz. Ayrıca, rust-wasm grubu WebAssembly ikili dosyalarını optimize etmek ve manipüle etmek için kullanılabilecek araçların bir listesini sağlamaktadır.