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.
Assembly dili
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 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ç, 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;
Derleme yolunu kullanmanın avantajı, bu tür düşük düzey ve makine için optimize edilmiş kodun, yüksek düzey ve insan 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 ama derlemenin 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ı bir sanal makineyi etkili bir şekilde tanımladı. Statik ve dinamik doğrulamanın bir kombinasyonu, JavaScript motorlarının geçerli asm.js kodu için önceden optimize edilmiş bir derleme stratejisi kullanmasına olanak tanıdı. Manuel bellek yönetimiyle statik olarak yazılmış dillerde (C gibi) yazılan kod, eski Emscripten (LLVM tabanlı) gibi 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ği ekledi. asm.js tarayıcılarda hâlâ çalışıyor olsa da WebAssembly tarafından yerini almıştır. Bu noktada asm.js'yi kullanmanın nedeni, WebAssembly desteği olmayan tarayıcılar için alternatif olarak kullanmaktır.
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 bellek yönetimi olan diller için destek çalışmaları devam ediyor ve yakında kullanıma sunulacak. Kotlin/Wasm'de olduğu gibi bu destek halihazırda kullanıma sunulmuştur. 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 olması ve korumalı bir ortamda çalışabilmesi 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ö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ı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 metinsel temsili görüyorsunuz.
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 el yazısıyla da 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 ü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 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 için 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
derleyicisiyle derleyebilirsiniz.
$ 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 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.
HTML sarmalayıcısı olmadan WebAssembly'e derlemenin 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
) Node.js ile birlikte çalıştırın:
$ node hello.js
Hello World
Daha fazla bilgi
WebAssembly'e bu kısa giriş, buzdağı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ıkları için biraz Nasıl baykuş çizimi yapılır? gibi hissettirebilirsiniz. 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.