WebAssembly nedir ve nereden geldi?

Web yalnızca dokümanlar için değil, aynı zamanda uygulamalar için de bir platform haline geldiğinden beri, en gelişmiş uygulamalardan bazıları web tarayıcılarını kendi sınırlarını zorladı. Performansı artırmak için alt düzey dillerle arayüz oluşturarak "metne daha yakın" olma yaklaşımı birçok üst düzey dilde karşınıza çıkmıştır. Örneğin, Java'da Java Yerel Arayüzü'ne sahip bir uygulama vardır. JavaScript için bu alt düzey dil WebAssembly'dir. Bu makalede, assembly dilinin ne olduğunu, web'de neden yararlı olabileceğini keşfedecek ve ardından, geçici asm.js çözümüyle WebAssembly'nin nasıl oluşturulduğunu öğreneceksiniz.

Assembly dili

Hiç Assembly dilinde program yaptınız mı? Bilgisayar programlamasında, genellikle basit bir şekilde Assembly olarak adlandırılan ve ASM veya asm olarak kısaltılan assembly dili, dildeki talimatlar ile mimarinin makine kodu talimatları arasında çok güçlü bir uyum bulunan herhangi bir düşük seviye programlama dilidir.

Örneğin, Intel® 64 ve IA-32 Mimarileri'ne (PDF) bakıldığında, MUL talimatı (çok işlemi için) ilk işlem gören ile ikinci işlenenin (kaynak işleneni) imzasız çarpımını gerçekleştirir ve sonucu hedef işlemde depolar. Çok basit bir şekilde ele alınan hedef işleneni, AX kaydında bulunan ima edilen bir işlenendir. Kaynak işlenen ise CX gibi genel amaçlı bir kayıtta bulunur. Sonuç yeniden AX kaydında depolanır. Şu x86 kod örneğini göz önünde bulundurun:

mov ax, 5  ; Set the value of register AX to 5.
mov cx, 10 ; Set the value of register CX to 10.
mul cx     ; Multiply the value of register AX (5)
           ; and the value of register CX (10), and
           ; store the result in register AX.

Karşılaştırma amacıyla, 5 ve 10'u çarpma görevi verildiyse, muhtemelen JavaScript'te aşağıdakine benzer bir kod yazarsınız:

const factor1 = 5;
const factor2 = 10;
const result = factor1 * factor2;

Montaj rotasına gitmenin avantajı, bu tür düşük seviyeli ve makine için optimize edilmiş kod, üst seviye ve insanlar için optimize edilmiş koddan çok daha verimli olmasıdır. Önceki durumda bu önemli değildir; ancak daha karmaşık işlemlerde bu farkın önemli olabileceğini düşünebilirsiniz.

Adından da anlaşılacağı gibi x86 kodu, x86 mimarisine bağlıdır. Belirli bir mimariye bağlı olmayan ama derlemenin performans avantajlarını devralacak bir derleme kodu yazma yöntemi olsaydı ne olurdu?

asm.js

Mimari bağımlılığı olmadan derleme kodu yazmanın ilk adımı, JavaScript'in katı bir alt kümesi olan asm.js'ydi. Bu alt küme, derleyiciler için alt düzey, verimli bir hedef dil olarak kullanılabilecekti. Bu alt dil, C veya C++ gibi bellek açısından güvenli olmayan diller için korumalı alana alınmış bir sanal makineyi etkili bir şekilde tanımladı. Statik ve dinamik doğrulamanın kombinasyonu, JavaScript motorlarının geçerli asm.js kodu için derleme stratejisini önceden optimize eden bir önceden (AOT) derleme stratejisi kullanmasına olanak tanıdı. Statik olarak yazılmış dillerde manuel bellek yönetimi (ör. C) kullanılarak yazılan kod, early Emscripten (LLVM'ye dayanır) gibi bir kaynaktan kaynağa derleyici tarafından çevrildi.

Dil özellikleri AOT'ya uygun özelliklerle sınırlandırılarak performans artırıldı. Firefox 22, OdinMonkey adı altında kullanıma sunulan ve asm.js'yi destekleyen ilk tarayıcıydı. Chrome, 61 sürümünde asm.js desteğini ekledi. asm.js hâlâ tarayıcılarda çalışsa da yerini WebAssembly aldı. Bu noktada asm.js kullanımı, WebAssembly desteği olmayan tarayıcılara alternatif olarak kullanılabilir.

WebAssembly

WebAssembly, neredeyse yerel performansla çalışan, C/C++ ve Rust gibi çok daha fazlasının web'de çalıştırılabilmesi için derleme hedefiyle çok daha fazlasını sunan kompakt bir ikili biçime sahip düşük seviyede, derleme benzeri bir dildir. Java ve Dart gibi bellekle yönetilen diller için destek çalışmaları devam etmektedir ve yakın zamanda kullanıma sunulacaktır veya Kotlin/Wasm'da olduğu gibi yaygınlaşmıştır. WebAssembly, JavaScript ile birlikte çalışacak şekilde tasarlanmıştır. Bu sayede her ikisinin de birlikte çalışması sağlanır.

WebAssembly programları, tarayıcının dışında, WebAssembly için modüler bir sistem arayüzü olan WebAssembly Sistem Arayüzü WASI sayesinde başka çalışma zamanlarında da çalışır. WASI, güvenli olması ve korumalı bir ortamda çalışabilmesi için işletim sistemleri arasında taşınabilir.

WebAssembly kodu (ikili kod, yani bayt kodu), taşınabilir sanal yığın makinesinde (VM) çalışacak şekilde tasarlanmıştır. Bayt kod, JavaScript'ten daha hızlı ayrıştırılıp yürütülmesi ve kompakt bir kod gösterimi sağlayacak şekilde tasarlanmıştır.

Talimatların kavramsal olarak yürütülmesi, talimatlarda ilerleyen geleneksel bir program sayacı aracılığıyla ilerler. Pratikte çoğu Wasm motoru, Wasm bayt kodunu makine kodu olarak derler ve ardından çalıştırır. Talimatlar iki kategoriye ayrılır:

  • Form kontrolü yapılarını oluşturan ve bunların bağımsız değişken değerlerini yığından çıkaran kontrol talimatları, program sayacını değiştirebilir ve sonuç değerlerini yığına aktarabilir.
  • Yığındaki bağımsız değişken değerlerini çıkan, değerlere bir operatör uygulayan, ardından sonuç değerlerini yığına aktaran, ardından program sayacının örtülü bir ilerlemesini sağlayan basit talimatlar.

Önceki örneğe dönersek, aşağıdaki WebAssembly kodu, makalenin başlangıcındaki x86 koduna eşdeğer olacaktır:

i32.const 5  ; Push the integer value 5 onto the stack.
i32.const 10 ; Push the integer value 10 onto the stack.
i32.mul      ; Pop the two most recent items on the stack,
             ; multiply them, and push the result onto the stack.

asm.js tamamen yazılım içinde uygulanmıştır. Diğer bir deyişle, kodu herhangi bir JavaScript motorunda (optimize edilmemiş olsa bile) çalışabilir. WebAssembly, tüm tarayıcı tedarikçilerinin kabul ettiği yeni işlevlere ihtiyaç duyuyordu. 2015'te duyurulan ve ilk olarak Mart 2017'de kullanıma sunulan WebAssembly, 5 Aralık 2019'da W3C önerisi oldu. W3C, tüm büyük tarayıcı tedarikçilerinin ve ilgili diğer tarafların katkılarıyla bu standardı korur. 2017'den beri tarayıcı desteği evrenseldir.

WebAssembly'nin iki temsili vardır: metin ve ikili. Yukarıda metinsel temsili görüyorsunuz.

Metinsel temsil

Metinsel gösterim S ifadelerine dayanır ve genellikle .wat dosya uzantısını kullanır (WebAssembly text biçimi için). Gerçekten isterseniz elinizle yazabilirsiniz. Yukarıdaki çarpma örneğini alıp artık faktörleri koda gömmeyerek daha kullanışlı bir hale getirmek için aşağıdaki kodu anlamlandırabilirsiniz:

(module
  (func $mul (param $factor1 i32) (param $factor2 i32) (result i32)
    local.get $factor1
    local.get $factor2
    i32.mul)
  (export "mul" (func $mul))
)

İkili temsil

.wasm dosya uzantısını kullanan ikili biçim, insan üretimi bir yana, insan tüketimine de yönelik değildir. wat2wasm gibi bir araç kullanarak yukarıdaki kodu aşağıdaki ikili gösterime dönüştürebilirsiniz. (Yorumlar genellikle ikili temsilin bir parçası değildir, ancak daha iyi anlaşılabilirlik için wat2wasm aracı tarafından eklenir.)

0000000: 0061 736d                             ; WASM_BINARY_MAGIC
0000004: 0100 0000                             ; WASM_BINARY_VERSION
; section "Type" (1)
0000008: 01                                    ; section code
0000009: 00                                    ; section size (guess)
000000a: 01                                    ; num types
; func type 0
000000b: 60                                    ; func
000000c: 02                                    ; num params
000000d: 7f                                    ; i32
000000e: 7f                                    ; i32
000000f: 01                                    ; num results
0000010: 7f                                    ; i32
0000009: 07                                    ; FIXUP section size
; section "Function" (3)
0000011: 03                                    ; section code
0000012: 00                                    ; section size (guess)
0000013: 01                                    ; num functions
0000014: 00                                    ; function 0 signature index
0000012: 02                                    ; FIXUP section size
; section "Export" (7)
0000015: 07                                    ; section code
0000016: 00                                    ; section size (guess)
0000017: 01                                    ; num exports
0000018: 03                                    ; string length
0000019: 6d75 6c                          mul  ; export name
000001c: 00                                    ; export kind
000001d: 00                                    ; export func index
0000016: 07                                    ; FIXUP section size
; section "Code" (10)
000001e: 0a                                    ; section code
000001f: 00                                    ; section size (guess)
0000020: 01                                    ; num functions
; function body 0
0000021: 00                                    ; func body size (guess)
0000022: 00                                    ; local decl count
0000023: 20                                    ; local.get
0000024: 00                                    ; local index
0000025: 20                                    ; local.get
0000026: 01                                    ; local index
0000027: 6c                                    ; i32.mul
0000028: 0b                                    ; end
0000021: 07                                    ; FIXUP func body size
000001f: 09                                    ; FIXUP section size
; section "name"
0000029: 00                                    ; section code
000002a: 00                                    ; section size (guess)
000002b: 04                                    ; string length
000002c: 6e61 6d65                       name  ; custom section name
0000030: 01                                    ; name subsection type
0000031: 00                                    ; subsection size (guess)
0000032: 01                                    ; num names
0000033: 00                                    ; elem index
0000034: 03                                    ; string length
0000035: 6d75 6c                          mul  ; elem name 0
0000031: 06                                    ; FIXUP subsection size
0000038: 02                                    ; local name type
0000039: 00                                    ; subsection size (guess)
000003a: 01                                    ; num functions
000003b: 00                                    ; function index
000003c: 02                                    ; num locals
000003d: 00                                    ; local index
000003e: 07                                    ; string length
000003f: 6661 6374 6f72 31            factor1  ; local name 0
0000046: 01                                    ; local index
0000047: 07                                    ; string length
0000048: 6661 6374 6f72 32            factor2  ; local name 1
0000039: 15                                    ; FIXUP subsection size
000002a: 24                                    ; FIXUP section size

WebAssembly için derleme

Gördüğünüz gibi, ne .wat ne de .wasm son derece kullanıcı dostu değildir. İşte Emscripten gibi bir derleyici burada devreye girer. C ve C++ gibi üst düzey dillerden derleme yapmanıza olanak tanır. Rust ve daha birçok dil için başka derleyiciler de vardır. Aşağıdaki C kodunu göz önünde bulundurun:

#include <stdio.h>

int main() {
  printf("Hello World\n");
  return 0;
}

Genellikle bu C programını gcc derleyicisi ile derlersiniz.

$ gcc hello.c -o hello

Emscripten yüklüyken, emcc komutunu ve hemen hemen aynı bağımsız değişkenleri kullanarak e-tabloyu WebAssembly'de derlersiniz:

$ emcc hello.c -o hello.html

Bu işlem bir hello.wasm dosyası ve hello.html HTML sarmalayıcı dosyası oluşturur. hello.html dosyasını bir web sunucusundan sunduğunuzda Geliştirici Araçları konsolunda "Hello World" yazdırılır.

Ayrıca, HTML sarmalayıcı olmadan WebAssembly'de derleme yapmanın bir yolu da vardır:

$ emcc hello.c -o hello.js

Önceden olduğu gibi bu işlem bir hello.wasm dosyası oluşturur, ancak bu kez HTML sarmalayıcı yerine bir hello.js dosyası oluşturulur. Test etmek için sonuç olarak elde edilen JavaScript dosyasını (hello.js) Node.js ile birlikte çalıştırın:

$ node hello.js
Hello World

Daha fazla bilgi

WebAssembly ile ilgili bu kısa giriş, buz dağının yalnızca görünen kısmıdır. MDN'deki WebAssembly dokümanlarında WebAssembly hakkında daha fazla bilgi edinin ve Emscripten dokümanlarına bakın. Assembly ile çalışmanın, özellikle HTML, CSS ve JavaScript konusunda bilgi sahibi olan web geliştiricileri C gibi derlenecek dillerden derlenme konusunda bilgi sahibi olmadığından biraz Nasıl baykuş çizimi yapılır? gibi hissettirebilirsiniz. Neyse ki StackOverflow'un webassembly etiketi gibi kanallar var. Uzmanlar kibar bir şekilde sorduğunuzda size yardımcı olmaktan genellikle memnuniyet duyacaklar.

Teşekkür

Bu makale Jakob Kummerow, Derek Schuff ve Rachel Andrew tarafından incelenmiştir.