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 itti. Performansı artırmak için alt düzey dillerle arayüz oluşturarak "mete daha yakın" olma yaklaşımı birçok üst düzey dilde karşılanmaktadır. Örneğin, Java'da Java Yerel Arayüzü 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 keşfedecek, ardından WebAssembly'nin geçici asm.js çözümüyle nasıl oluşturulduğunu öğreneceksiniz.

Assembly dili

Hiç assembly dilinde programladınız mı? Bilgisayar programlamasında, genellikle Assembly olarak anılan ve genellikle ASM veya asm olarak kısaltılan montaj dili, dildeki talimatlar ile mimarinin makine kodu talimatları arasında çok güçlü bir eşleşmenin bulunduğu düşük seviyeli herhangi bir programlama dilidir.

Örneğin, Intel® 64 ve IA-32 Mimarileri'ne (PDF) bakıldığında MUL talimatı (mul ipi işlemi için), ilk işlenenin (hedef işlenen) ile ikinci işlenenin (kaynak işlenenin) imzasız çarpmasını gerçekleştirir ve sonucu hedef işlenende depolar. Çok basittir. Hedef işlenen, AX kaydında bulunan örtük işlenen bir değerdir ve kaynak işleneni CX gibi genel amaçlı bir kayıtta bulunur. Sonuç tekrar AX kaydında saklanır. Aşağıdaki x86 kodu ö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, görevi 5 ile 10'u çarpma görevi veriliyorsa muhtemelen JavaScript'te aşağıdakine benzer bir kod yazarsınız:

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

Derleme rotasını tercih etmenin avantajı, bu tür düşük seviyeli ve makine için optimize edilmiş kodların, üst düzey ve kullanıcılar için optimize edilmiş koddan çok daha verimli olmasıdır. Önceki örnekte bunun önemi yoktur, ancak daha karmaşık işlemlerde farkın önemli olabileceğini düşünebilirsiniz.

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

asm.js

Hiçbir mimari bağımlılığı olmadan derleme kodu yazmanın ilk adımı, derleyiciler için düşük seviyeli ve verimli bir hedef dil olarak kullanılabilen katı bir JavaScript alt kümesi olan asm.js'dir. 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 açıkladı. Statik ve dinamik doğrulamanın bir kombinasyonu, JavaScript motorlarının, geçerli asm.js kodu için derleme stratejisini önceden optimize etme (AOT) uygulamasına olanak tanıdı. Manuel bellek yönetimiyle (ör. C) statik olarak yazılan dillerde yazılan kod, erken Emscripten (LLVM'ye dayalı) gibi bir kaynaktan kaynağa derleyici tarafından çevrilmiştir.

Dil özellikleri, AOT'ye uygun özelliklerle sınırlanarak performans iyileştirildi. Firefox 22, OdinMonkey adıyla kullanıma sunulan ve asm.js'yi destekleyen ilk tarayıcıdır. Chrome, sürüm 61'de asm.js desteğini ekledi. Asm.js hâlâ tarayıcılarda çalışıyor olsa da yerini WebAssembly aldı. Bu noktada, WebAssembly desteği olmayan tarayıcılar için bir alternatif olarak asm.js kullanılabilir.

WebAssembly

WebAssembly, neredeyse yerel performansla çalışan, C/C++ ve Rust gibi dillerin yanı sıra daha birçok dili, web'de çalıştırılmak üzere bir derleme hedefiyle sağlayan kompakt bir ikili biçime sahip, alt düzey derleme benzeri bir dildir. Java ve Dart gibi bellekle yönetilen diller için destek şu anda devam etmektedir ve yakında kullanıma sunulacaktır veya Kotlin/Wasm'da olduğu gibi zaten gelmiştir. WebAssembly, JavaScript ile birlikte çalışacak şekilde tasarlanmıştır. Böylece her ikisinin birlikte çalışması sağlanır.

WebAssembly programları, tarayıcı dışında ayrıca 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ı alana alınmış 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ıştırılacak şekilde tasarlanmıştır. Bayt kodu, JavaScript'e göre daha hızlı ayrıştırılıp yürütülecek ve kompakt bir kod temsiline sahip olacak şekilde tasarlanmıştır.

Talimatların kavramsal olarak yürütülmesi, talimatlarda ilerleyen geleneksel bir program sayacı yoluyla gerçekleşir. Pratikte, çoğu Wasm motoru, Wasm bayt kodunu makine koduna derler ve ardından yürütür. Talimatlar iki kategoriye ayrılır:

  • 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.
  • Bağımsız değişken değerlerini yığında açan, değerlere bir operatör uygulayan, ardından sonuç değerlerini yığına aktaran ve ardından program sayacında dolaylı bir ilerleme izleyen basit talimatlar.

Önceki örneğe dönersek, aşağıdaki WebAssembly kodu, makalenin başı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ıma uygulanırken, yani kodu herhangi bir JavaScript motorunda (optimize edilmemiş olsa bile) çalıştırılabilirken WebAssembly, tüm tarayıcı tedarikçilerinin kabul ettiği yeni işlevler gerektiriyordu. 2015'te duyurulan ve ilk olarak Mart 2017'de yayınlanan WebAssembly, 5 Aralık 2019'da W3C önerisi haline geldi. W3C, tüm büyük tarayıcı satıcılarının ve ilgili diğer tarafların katkılarıyla bu standardı korumaktadır. 2017'den beri tarayıcı desteği evrenseldir.

WebAssembly iki temsile sahiptir: metin ve ikili. Yukarıda gördüğünüz metin temsilidir.

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 istiyorsanız elle yazabilirsiniz. Yukarıdaki çarpma örneğini alıp çarpanlara kod girmeyecek şekilde daha kullanışlı hale getirirsek, büyük olasılıkla 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 biçimi, insan üretimi bir yana, insan tüketimine yönelik değildir. wat2wasm gibi bir araç kullanarak yukarıdaki kodu aşağıdaki ikili temsile dönüştürebilirsiniz. (Yorumlar genellikle ikili temsilin bir parçası değildir ancak daha iyi anlaşılması için wat2wasm aracı tarafından eklenmiştir.)

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'ye derleme

Gördüğünüz gibi ne .wat ne de .wasm ne de çok insan dostu özellikler. İşte Emscripten gibi bir derleyici burada devreye girer. C ve C++ gibi üst düzey dillerden derleme yapmanıza olanak tanır. Rust gibi başka diller 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 derleyicisiyle derlersiniz.

$ gcc hello.c -o hello

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

$ emcc hello.c -o hello.html

Bu işlem sonucunda bir hello.wasm dosyası ve hello.html HTML sarmalayıcı dosyası oluşturulur. hello.html dosyasını bir web sunucusundan sunduğunuzda, "Hello World" dosyasının DevTools konsoluna yazdırıldığını görürsünüz.

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

$ emcc hello.c -o hello.js

Daha önce olduğu gibi bu işlem bir hello.wasm dosyası oluşturacak, ancak bu sefer HTML sarmalayıcı yerine hello.js dosyası oluşturacaktır. Test etmek için, sonuçta oluşturulan JavaScript dosyasını hello.js ile örneğin Node.js ile çalıştırıyorsunuz:

$ 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. Gerçeği söylemek gerekirse, WebAssembly ile çalışmak, özellikle HTML, CSS ve JavaScript'e aşina olan web geliştiricilerin C gibi derlenecek kaynaklardan derlenecek dil konusunda çok bilgi sahibi olmadığı için biraz baykuş meme'i çizme'ye benzer hisler verebilir. Neyse ki StackOverflow'un webassembly etiketi gibi kanallar var. İyi sorular sorduğunuzda uzmanlar size memnuniyetle yardımcı olurlar.

Teşekkür

Bu makale Jakob Kummerow, Derek Schuff ve Rachel Andrew tarafından incelendi.