WebAssembly nedir ve nereden geldi?

Web, yalnızca dokümanlar için değil, uygulamalar için de bir platform haline geldiğinden beri en gelişmiş uygulamalardan bazıları web tarayıcılarını sınırlarına kadar zorladı. Performansı artırmak için daha düşük düzey dillerle arayüz oluşturarak "metale daha yakın" olma yaklaşımına birçok üst düzey dilde rastlanır. Örneğin, Java'da Java Native Interface vardır. JavaScript için bu alt düzey dil WebAssembly'dir. Bu makalede, derleme dilinin ne olduğunu ve web'de neden yararlı olabileceğini öğreneceksiniz. Ardından, asm.js geçici çözümü aracılığıyla WebAssembly'in nasıl oluşturulduğunu öğreneceksiniz.

Daha önce hiç assembly dilinde program yazdınız mı? Bilgisayar programlamasında, genellikle Assembly olarak adlandırılan ve genellikle ASM veya asm olarak kısaltılan derleme dili, dildeki talimatlar ile mimarinin makine kodu talimatları arasında çok güçlü bir ilişki bulunan herhangi bir düşük seviyeli programlama dilidir.

Örneğin, Intel® 64 ve IA-32 Mimarileri'ne (PDF) baktığımızda MUL talimatı (çarpma için), ilk operandın (hedef operand) ve ikinci operandın (kaynak operand) işaretsiz çarpımını gerçekleştirir ve sonucu hedef operandta depolar. Çok basitleştirilmiş bir şekilde, hedef operand AX yazmaçında bulunan bir ima operandı, kaynak operand ise CX gibi genel amaçlı bir yazmaçta bulunur. Sonuç, AX kaydına tekrar kaydedilir. Aşağıdaki x86 kod örneğini inceleyin:

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 ile 10'u çarpma görevi verilse JavaScript'te muhtemelen aşağıdakine benzer bir kod yazarsınız:

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

Montaj rotasına gitmenin avantajı, düşük seviyeli ve makine için optimize edilmiş bu kadar yüksek seviyeli ve insanlar için optimize edilmiş koddan çok daha verimli olmasıdır. Önceki örnekte bu fark önemli değildir ancak daha karmaşık işlemlerde farkın önemli olabileceğini tahmin edebilirsiniz.

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

asm.js

Mimari bağımlılığı olmayan bir derleme kodu yazmanın ilk adımı, derleyiciler için düşük düzeyde, verimli bir hedef dil olarak kullanılabilen JavaScript'in katı bir alt kümesi olan asm.js idi. 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ı. Manuel bellek yönetimiyle (ör. C) statik olarak yazılmış dillerde yazılan kod, early Emscripten (LLVM'ye dayanır) gibi bir kaynaktan kaynağa derleyici tarafından çevrildi.

Dil özellikleri, AOT'ye uygun olanlarla sınırlandırılarak performans iyileştirildi. Firefox 22, OdinMonkey adı altında yayınlanan asm.js'yi destekleyen ilk tarayıcıydı. Chrome, 61 sürümünde asm.js desteğini ekledi. asm.js hala tarayıcılarda çalışsa da yerini WebAssembly almıştır. Bu noktada asm.js kullanımı, WebAssembly desteği olmayan tarayıcılara alternatif olarak kullanılabilir.

WebAssembly

WebAssembly, yerele yakın performansla çalışan, kompakt bir ikili biçime sahip, düşük seviyeli, assembly benzeri bir dildir. C/C++ ve Rust gibi dillerin yanı sıra web'de çalışacak şekilde derleme hedefi olan daha birçok dili destekler. 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.

WebAssembly programları, tarayıcı dışında WebAssembly için modüler bir sistem arayüzü olan WebAssembly Sistem Arayüzü (WASI) sayesinde diğer çalışma zamanlarında da çalışır. WASI, güvenli olmak ve korumalı bir ortamda çalışabilmek amacıyla işletim sistemleri arasında taşınabilir olacak şekilde tasarlanmıştır.

WebAssembly kodu (ikili kod, yani bayt kodu), taşınabilir bir sanal yığın makinesinde (VM) çalışacak şekilde tasarlanmıştır. Bytecode, ayrıştırma ve yürütme işleminin JavaScript'den daha hızlı olması ve kompakt bir kod temsiline sahip olması için tasarlanmıştır.

Talimatların kavramsal olarak yürütülmesi, talimatlar arasında ilerleyen geleneksel bir program sayacı aracılığıyla gerçekleşir. Uygulamada çoğu Wasm motoru, Wasm bayt kodunu makine koduna derleyip çalıştırır. Talimatlar iki kategoriye ayrılır:

  • Kontrol yapılarını oluşturan ve 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 ekleyebilir.
  • Bağımsız değişken değerlerini yığından çıkaran, değerlere bir operatör uygulayan ve ardından sonuç değerlerini yığına iten ve ardından program sayacını örtülü olarak ilerleten basit talimatlar.

Önceki örneğe dönecek olursak aşağıdaki WebAssembly kodu, makalenin başındaki x86 koduna eşdeğerdir:

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ımda uygulanır. Yani kodu, optimize edilmemiş olsa bile herhangi bir JavaScript motorunda çalışabilir. WebAssembly ise tüm tarayıcı tedarikçilerinin üzerinde anlaştığı yeni işlevler gerektiriyordu. 2015'te duyurulan ve ilk olarak Mart 2017'de yayınlanan WebAssembly, 5 Aralık 2019'da W3C önerisi oldu. W3C, tüm büyük tarayıcı tedarikçi firmalarının ve diğer ilgili tarafların katkılarıyla standardı korur. 2017'den beri tarayıcı desteği evrenseldir.

WebAssembly'in iki temsili vardır: metinsel ve ikili. Yukarıda gördüğünüz metin temsilidir.

Metin temsili

Metin temsili S ifadelerinden oluşur 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 ele alıp faktörleri artık sabit kodlayarak daha kullanışlı hale getirdiğinizde aşağıdaki kodun ne anlama geldiğini anlayabilirsiniz:

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

İkili gösterim

.wasm dosya uzantısını kullanan ikili biçim, insan tarafından oluşturulması bir yana, insan tarafından kullanılması için tasarlanmamıştır. wat2wasm gibi bir araç kullanarak yukarıdaki kodu aşağıdaki ikili gösterime dönüştürebilirsiniz. (Yorumlar genellikle ikili gösterimin 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'e derleme

Gördüğünüz gibi, .wat ve .wasm kullanıcılar için pek uygun değil. Bu noktada Emscripten gibi bir derleyici 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 ele alalım:

#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ü olduğunda, emcc komutunu ve neredeyse aynı bağımsız değişkenleri kullanarak WebAssembly'e derleyebilirsiniz:

$ emcc hello.c -o hello.html

Bu işlem, bir hello.wasm dosyası ve HTML sarmalayıcı dosyası hello.html 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

Bu işlem, önceki gibi bir hello.wasm dosyası oluşturur ancak bu kez HTML sarmalayıcısı yerine hello.js dosyası oluşturur. Test etmek için, elde edilen JavaScript dosyasını hello.js örneğin Node.js ile ç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 göz atın. Doğruyu söylemek gerekirse, WebAssembly ile çalışmak biraz Baykuş çizme meme'si gibi gelebilir. Özellikle de HTML, CSS ve JavaScript'e aşina web geliştiricileri, C gibi derlenecek dillerde bilgili olmayabilir. Neyse ki StackOverflow'un webassembly etiketi gibi kanallar var. Bu kanallarda, kibarca sorarsanız uzmanlar size yardımcı olmaktan memnuniyet duyar.

Teşekkür ederiz

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