Screenshot 2023-03-23 134010

WebAssembly (Einsteiger*innen-Workshop im WS2022/23)

Von am 23.03.2023

Warum WebAssembly?

In den meisten klassischen Web-Applikationen, an deren Entwicklung ich beteiligt war, stellte das de-facto Monopol der Sprache JavaScript im Frontend kein Problem dar. Weder war das Bedürfnis nach Performance-Optimierung in einem solchen Maße gegeben, dass die Suche nach einer Lösung außerhalb des JavaScript-Ökosystems notwendig war, noch wurde die Beschränkung auf JavaScript-Librarys und -Frameworks als etwas angesehen, das man realistischerweise umgehen kann. Doch welchen Weg könnte man einschlagen, sollte das plötzlich in einem Projekt nicht mehr zutreffen? Die Antwort darauf könnte WebAssembly lauten. Um mir selbst und der Masterklasse Mobile einen Überblick über das Thema und einen Einblick in potenzielle neue Problemlösungsstrategien für die erwähnten Problemstellungen zu geben, hielt ich einen Workshop, dessen Inhalte ich im Folgenden zusammenfassend dokumentieren möchte.

Ziele und Nicht-Ziele des Workshops

Ziele:

  • Das Konzept von WebAssembly veranschaulichen
    • Mögliche Einsatzbereiche ausfindig machen
    • Alle – auch mich selbst – mit neuem Wissen bereichern

Nicht-Ziele:

  • Neue Programmiersprachen lernen
    • Große Applikationen bauen
    • Eine reine Frontal-Präsentation abhalten

Die Problemstellung im Detail

Wie erwähnt ist unsere Ausgangslage, dass Browser grundsätzlich nur JavaScript als Frontend-Programmiersprache ausführen können. Dies hat erhebliche Nachteile im Hinblick auf Performance, denn JavaScript ist eine interpretierte Sprache (im Gegensatz zu kompilierten Sprachen). Konkret heißt das, dass der Code nicht zuerst in Maschinensprache umgewandelt werden muss, sondern direkt im Browser zur Laufzeit übersetzt wird. Hier setzt WebAssembly an. Die Performance von Webanwendungen soll durch das browserseitige Ausführen vorher kompilierten Codes verbessert werden, denn es entfällt der Extra-Schritt der Übersetzung zur Laufzeit. Jedoch ist der Einsatz von WebAssembly nicht auf den Webbrowser beschränkt. Auch serverseitig und in Stand-Alone-Runtimes ist es möglich, dieselbe Technologie einzusetzen.

Was ist nun WebAssembly genau?

Von w3.org wird WebAssembly (kurz WASM) als binäres Instruktionsformat für eine Stack-basierte virtuelle Maschine bezeichnet. Das Ziel ist es also, eine im Browser vorhandene virtuelle Maschine mit Instruktionen in einem bestimmten standardisierten Format zu versorgen. Diese ist als Stack-Machine konzipiert, d. h. es kann jeweils nur auf den obersten Eintrag ein einem Stapel an gespeicherten Werten zugegriffen werden. Dies soll uns aber als Web-Entwickler*innen nicht zu sehr abschrecken, denn im Normalfall ist es Aufgabe des Compilers, lauffähige Instruktionen in Maschinensprache zu erzeugen.

Wir benötigen also einen geeigneten Compiler, um für unser Nicht-JavaScript-Projekt ein WASM-File zu erzeugen. Hiervon gibt es bereits einige, wie z. B. „emscripten“ für C und C++, „Pyodide“ für Python, Rust, und AssemblyScript für eine syntaktisch auf TypeScript beruhenden Sprache. Zumal WebAssembly standardisiert ist, können Compiler für so gut wie alle erdenklichen Programmiersprachen entwickelt werden.

Code-Beispiele

Zusätzlich zu WASM-Dateien generieren viele Compiler auch WAT-Dateien (WebAssembly Text Format), welche als für den Menschen lesbare Variante des Outputs diesen. Zwar kann man wie im folgenden Beispiel auch selbst WAT-Dateien schreiben und diese dann in WASM umwandeln, jedoch dient dieses Format in der Praxis in erster Linie dem Debugging.

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add))
)

Hier wird eine simple Additions-Funktion implementiert, welche die Summe der beiden Parameter „lhs“ und „rhs“ liefert. Sie wird dann exportiert, sodass im Frontend mit JavaScript das definierte Modul geladen und ein Funktionsaufruf getätigt werden kann. Letzteres könnte dann wie folgt umgesetzt werden:

fetch("foo.wasm")
  .then((res) => res.arrayBuffer())
  .then((bytes) => WebAssembly.instantiate(bytes))
  .then((res) => {
    const result = res.instance.exports.add(5, 7);
  });

Zu beachten ist, dass in diesem Beispiel die WASM-Datei „foo.wasm“ mittels HTTP-Request vom Server geladen wird. Es wäre aber auch denkbar, den Bytecode einer kompilierten WASM-Datei direkt als Literal in JavaScript an den Client mitzugeben. Auch sei an dieser Stelle festgestellt, dass WebAssembly keinen gänzlichen Ersatz für JavaScript darstellt, zumal ein WASM-Modul aus JavaScript-Code heraus aufgerufen wird. Es handelt sich also grundsätzlich um eine Möglichkeit, rechenintensive Prozesse auszulagern, während der Ausgangspunkt immer noch eine JavaScript-Anwendung bleibt.

Anwendungsfälle

Es wurde bereits eine Vielzahl an Projekten mit WebAssembly umgesetzt. Ein prominentes Beispiel stellt die von Google entwickelte App „Squoosh“ dar, welche es erlaubt, Bilddateien hochzuladen und diese clientseitig in ein anderes Format umzuwandeln. So kann der Server dahingehend entlastet werden, dass die Umwandlung von z. B. JPEG nach WebP mit vom User festgelegten Qualitätsparametern clientseitig erfolgt und auf Funktionalität zurückgegriffen werden kann, die aus einer Nicht-JavaScript-Library stammt. Ferner wurden bereits Spiele, welche ursprünglich für Spielkonsolen entwickelt wurden, oder 3D- bzw. Grafikprogramme mittels WebAssembly browserfähig gemacht.

Fazit

WebAssembly ist eine Technologie, welche die Lösung für einige spezifische Problemstellungen liefert, und potenziell performante und sichere Web-Apps hervorbringen kann bzw. dies bereits unter Beweis gestellt hat. Jedoch sollte hierbei beachtet werden, dass es sich nicht um eine Konkurrenz-Technologie zu herkömmlichen JavaScript-Apps handelt, sondern viel mehr um eine Ergänzung, welche das Auslagern rechenintensiver Prozesse in eine kompilierte Datei ermöglicht. Es wäre also gegenwärtig verfrüht, das Ende von JavaScript zu proklamieren.

The comments are closed.