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üzeydeki dillerle arayüz oluşturarak "donanıma daha yakın" olma yaklaşımı, birçok üst düzey dilde görülür. Örneğin, Java'da Java Native Interface bulunur. JavaScript için bu alt düzey dil WebAssembly'dir. Bu makalede, makine dilinin ne olduğunu ve web'de neden faydalı olabileceğini öğrenecek, ardından WebAssembly'nin asm.js'nin ara çözümü aracılığıyla nasıl oluşturulduğunu keşfedeceksiniz.
Assembly dili
Daha önce hiç assembly dilinde program yazdınız mı? Bilgisayar programlamada, genellikle kısaca Assembly olarak adlandırılan ve yaygın olarak ASM veya asm şeklinde kısaltılan Assembly dili, dilindeki talimatlar ile mimarinin makine kodu talimatları arasında çok güçlü bir yazışma olan herhangi bir düşük seviyeli programlama dilidir.
Örneğin, Intel® 64 and IA-32 Architectures (PDF) belgesine baktığımızda MUL
talimatının (çarpma için) birinci işlenenin (hedef işlenen) ve ikinci işlenenin (kaynak işlenen) işaretsiz çarpma işlemini gerçekleştirdiğini ve sonucu hedef işlenende sakladığını görüyoruz. Çok basitleştirilmiş bir şekilde, hedef işlenen, AX
kaydında bulunan örtülü bir işlenendir ve kaynak işlenen, 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 için, 5 ile 10'u çarpma görevi verildiğinde 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 seviyeli ve makine için optimize edilmiş kodların, yüksek seviyeli ve insan için optimize edilmiş kodlardan çok daha verimli olmasıdır. Önceki örnekte bu durum önemli olmasa da daha karmaşık işlemler için farkın önemli olabileceğini tahmin edebilirsiniz.
Adından da anlaşılacağı üzere x86 kodu, x86 mimarisine bağlıdır. Belirli bir mimariye bağlı olmayan ancak derleme dilinin performans avantajlarını devralan bir derleme kodu yazma yöntemi olsaydı ne olurdu?
asm.js
Mimari bağımlılıkları olmayan derleme kodu yazmanın ilk adımı, derleyiciler için düşük seviyeli ve 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 belleğin güvenli olmadığı diller için etkili bir şekilde korumalı alan sanal makinesini tanımlıyordu. Statik ve dinamik doğrulamanın birleşimi, JavaScript motorlarının geçerli asm.js kodu için önceden (AOT) optimize eden bir derleme stratejisi kullanmasına olanak tanıyordu. Bellek yönetimi manuel olarak yapılan, statik olarak türü belirlenmiş dillerde (ör. C) yazılan kodlar, erken Emscripten (LLVM tabanlı) gibi bir kaynak-kaynak derleyici tarafından çevrilmiştir.
Dil özellikleri, AOT'ye uygun olanlarla sınırlandırılarak performans iyileştirildi. Firefox 22, asm.js'yi destekleyen ilk tarayıcıydı ve OdinMonkey adıyla yayınlandı. Chrome, 61. sürümde asm.js desteği ekledi. asm.js tarayıcılarda çalışmaya devam etse de yerini WebAssembly 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, derleme hedefi olarak web'i kullanan C/C++ ve Rust gibi dillerin web'de çalışmasını sağlayan, yerel performansa yakın bir performansla çalışan ve derlenmiş hedefi web olan, kompakt bir ikili biçime sahip, düşük seviyeli bir assembly benzeri dildir. Java ve Dart gibi bellek yönetimi yapılan diller için destek üzerinde çalışılmaktadır ve bu destek yakında kullanıma sunulacaktır. Kotlin/Wasm'de olduğu gibi, destek zaten kullanıma sunulmuş da olabilir. WebAssembly, JavaScript ile birlikte çalışacak şekilde tasarlanmıştır ve ikisinin birlikte çalışmasına olanak tanır.
WebAssembly programları, tarayıcının yanı sıra 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, işletim sistemleri arasında taşınabilir olacak şekilde oluşturulmuştur. Güvenli olma ve korumalı alana alınmış bir ortamda çalışabilme hedeflenir.
WebAssembly kodu (ikili kod, yani bayt kodu), taşınabilir bir sanal yığın makinesinde (VM) çalıştırılmak üzere tasarlanmıştır. Bayt kodu, JavaScript'ten daha hızlı ayrıştırılıp yürütülecek ve kompakt bir kod gösterimine sahip olacak şekilde 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 derler ve ardından bu kodu yürütür. Talimatlar iki kategoriye ayrılır:
- Form kontrol yapıları 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 gönderebilir.
- 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 gönderen basit talimatlar. Bunu, program sayacının örtülü olarak ilerlemesi izler.
Önceki örneğe dönecek olursak aşağıdaki WebAssembly kodu, makalenin başındaki x86 koduna eşdeğer olur:
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ımla uygulanırken (yani kodu, optimize edilmemiş olsa bile herhangi bir JavaScript motorunda çalışabilir), WebAssembly için tüm tarayıcı satıcılarının üzerinde anlaştığı yeni işlevler gerekiyordu. 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ı sağlayıcılarının ve diğer ilgili tarafların katkılarıyla standardı korur. 2017'den beri tarayıcı desteği evrenseldir.
WebAssembly'nin iki temsili vardır: metinsel ve ikili. Yukarıda gördüğünüz, metin biçimindeki gösterimdir.
Metin gösterimi
Metin gösterimi, S-expressions'a dayanır ve genellikle .wat
dosya uzantısını (WebAssembly text format için) kullanır. İsterseniz bunu elle de yazabilirsiniz. Yukarıdaki çarpma örneğini ele alıp faktörleri artık sabit kodlamayarak daha kullanışlı hale getirdiğinizde aşağıdaki kodu 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 tüketilmek 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şılabilmesi 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'ye derleme
Gördüğünüz gibi, .wat
ve .wasm
ifadeleri insan dostu değildir. Bu noktada Emscripten gibi bir derleyici devreye girer.
C ve C++ gibi üst düzey dillerden derleme yapmanıza olanak tanır. Rust gibi diğer diller için de başka derleyiciler vardır. Aşağıdaki C kodunu inceleyin:
#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üklendikten sonra, emcc
komutunu ve neredeyse aynı bağımsız değişkenleri kullanarak WebAssembly'ye derlersiniz:
$ emcc hello.c -o hello.html
Bu işlem, hello.wasm
dosyasını ve HTML sarmalayıcı dosyasını hello.html
oluşturur. hello.html
dosyasını bir web sunucusundan sunduğunuzda, Geliştirici Araçları konsoluna "Hello World"
yazdırılır.
HTML sarmalayıcısı olmadan WebAssembly'ye derlemenin de bir yolu vardır:
$ emcc hello.c -o hello.js
Bu işlem, daha önce olduğu gibi bir hello.wasm
dosyası oluşturur ancak bu kez HTML sarmalayıcı yerine hello.js
dosyası oluşturur. Test etmek için, sonuçta elde edilen JavaScript dosyasını hello.js
örneğin Node.js ile çalıştırırsınız:
$ node hello.js
Hello World
Daha fazla bilgi
WebAssembly ile ilgili bu kısa tanıtım, konunun yalnızca küçük bir bölümünü kapsamaktadır.
WebAssembly hakkında daha fazla bilgiyi MDN'deki WebAssembly belgelerinde bulabilir ve Emscripten belgelerine göz atabilirsiniz. WebAssembly ile çalışmak, özellikle HTML, CSS ve JavaScript'e aşina olan web geliştiriciler C gibi derlenecek diller konusunda uzman olmadığından, Baykuş nasıl çizilir? memi ile çalışmaya biraz benzeyebilir. Neyse ki StackOverflow'un webassembly
etiketi gibi kanallar var. Bu kanallarda uzmanlar, kibarca sorarsanız genellikle yardımcı olmaktan memnun olurlar.
Teşekkür
Bu makale Jakob Kummerow, Derek Schuff ve Rachel Andrew tarafından incelenmiştir.