Je to asi měsíc, co Brendan Eich (autor JavaScriptu) zveřejnil na svém blogu oznámení o WebAsembly, binárním formátu pro vykonavatelný kód, který by měl v budoucnu koexistovat v prohlížečích vedle JavaScriptu. Pokud projekt nevyzní do ztracena (a zdá se, že má všechny předpoklady pro to, aby se to nestalo), jedná se zřejmě o poměrně velkou novinku.
Tato novinka ovšem nepřichází jako blesk z čistého nebe, předcházela jí víceletá snaha různých stran o to, aby prohlížeče podporovaly i jiné jazyky než JavaScript - ať už nativně (vzpomeňme třeba ve své době velké ambice jazyka Dart od Googlu) nebo pomocí kompilace. Je to právě současný přístup ke kompilaci do JavaScriptu, konkrétně asm.js, ze kterého WebAssembly vychází, a který se snaží posunout zase o krok dále. Pojdme si tedy o asm.js říci něco bližšího.
LLVM a Emscripten
Než se dostaneme k samotnému asm.js, je třeba se zmínit ještě o projektu LLVM, což je sada nástrojů pro optimalizaci během kompilace a generování nativního kódu. Klasická kompilace jazyků jako je C nebo C++ do binární formy (v tomto případě pomocí LLVM) zjednodušeně funguje nějak takhle:
- Frontend je komponenta zodpovědná za převod původního jazyka (např. C, C++, ale i jiných) do jakéhosi mezijazyka, tzv. Intermediate Representation (IR). Každý zdrojový jazyk má typicky svůj frontend.
- Optimizer optimalizuje program; na vstupu i na výstupu je IR.
- Backend je zodpovědný za převod IR do nativního kódu. Každá CPU architektura má svůj backend.
Je nutné, aby backend generoval nativní kód? Ukazuje se, že není - pro LLVM je možné napsat jakýkoli backend, který přeloží IR do libovolné jiné formy. Takovým backendem je i Emscripten, který místo nativního kódu generuje JavaScript. To znamená, že můžeme vzít program napsaný, dejme tomu, v C++, přeložit ho pomocí Emscripten do JavaScriptu a pustit v prohlížeči. Schematicky znázorněno:
A zde již do hry vstupuje asm.js.
Asm.js
Jak již bylo řečeno, LLVM backendy typicky generují strojový kód, který se v zásadě skládá z jednoduchých aritmetických operací. Emscripten sice generuje JavaScript, ale ani tento kód neobsahuje nic než relativně jednoduché instrukce. Jinými slovy, celá škála dynamických vlastností JavaScriptu, dynamickým typováním počínaje a garbage collection konče, není v kódu, který vytvoří Emscripten, vůbec využita.
Toho si všimli v Mozille a napadlo je - pokud bychom mohli počítat s tím, že kus Javascriptu nevyužívá dynamické vlastnosti jazyka, mohli bychom výrazně zrychlit jeho vykonávání. V tom případě totiž javascriptový engine může přeskočit velké množství různých runtime kontrol, a dokonce si může kusy kódu předkompilovat.
Výsledkem byl projekt asm.js - jasně definovaná podmnožina JavaScriptu, která nevyužívá dynamických vlastností jazyka, a může proto běžet výrazně rychleji než “obvyklý” JavaScript, pokud je tomu daný javascriptový engine uzpůsoben. Díky tomu, že se stále jedná o platný JavaScript, odpadají problémy se zpětnou kompatibilitou - prohlížeče, které asm.js nepodporují, mohou asm.js kód zpracovávat stejně jako jakýkoli jiný JavaScript.
Podobně jako jsme zvyklí používat ‘use strict’ direktivu, je možné na začátku modulů a funkcí deklarovat použití asm.js pomocí direktivy ‘use asm’.
Narazí-li prohlížeč podporující asm.js na tuto direktivu, ověří, zda se jedná o validní asm.js kód, a následně jej vykoná s příslušnými optimalizacemi. Asm.js dnes podporuje především Mozilla Firefox, ale do jisté míry (čti: dojde ke zrychlení, ale k méně dramatickému) i Chrome a výhledově i Internet Explorer. Podle benchmarků se zdá, že ve Firefoxu běží asm.js kód jen asi 1,5x pomaleji než nativní verze.
Shrnuto: dnes tedy můžeme psát kód pro web v různých jazycích (při využtí asm.js asi nejsnáz stále v C/C++), který v prohlížeči poběží téměř tak rychle jako by běžel nativně. Dá se to ještě zlepšit?
WebAssembly
Asm.js má i nevýhody:
- Z koncepčního hlediska se zdá přinejmenším podezřelé “schovávat” nízkoúrovňový jazyk do JavaScriptu.
- Výsledek pořád ještě není tak rychlý jak by mohl být. Brendan Eich ve svém úvodním příspěvku píše, že dekomprese a parsování asm.js kódu jsou tím, co výsledný uživatelský zážitek momentálně nejvíce zpomaluje.
- Sémantika JavaScriptu jakožto kompilačního cíle může být z hlediska některých zdrojových jazyků omezující.
Tyto problémy by měl řešit nový standard nazvaný WebAssembly. Jedná se o binární formát pro nízkoúrovňový kód, který v první fázi bude koexpresivní s asm.js (aby umožnil konverzi WebAssembly do asm.js v prohlížečích, které web assembly třeba nebudou zpočátku podporovat), ale v budoucnu se předpokládá jeho další rozvoj za hranice možností sémantiky JavaScriptu.
První výsledky vypadají slibně - viz tento článek, kde se mimo jiné dočteme:
Experimenting with a prototype WebAssembly format on a build of our AngryBots demo, we saw the size of the generated JavaScript code go from 19.0 MB of asm.js code (gzip-compressed to 4.1 MB) down to 6.3 MB of WebAssembly code (gzip-compressed to 3.0 MB). This means that the amount of data the browser needs to process gets reduced by 3.0x, and the compressed download size gets reduced by 1.4x.
Připočtěme k tomu fakt, že na standardu WebAssembly údajně spolupracují Mozilla, Google i Microsoft, a myslím, že je jasné, že potenciál posunout web zase o krok dál má tento projekt opravdu velký.
Pokud vás to zaujalo, můžete si přečíst tento článek, který o WebAssembly pojednává o něco podrobněji.