2 Punkte von GN⁺ 2024-04-15 | 1 Kommentare | Auf WhatsApp teilen

Die Grenzen von WebAssembly und die Bedeutung von Tree-Shaking

  • Trotz viel Aufmerksamkeit und hoher Erwartungen war WebAssembly im Web nur begrenzt erfolgreich

    • Es gibt Erfolgsbeispiele wie Photoshop, insgesamt ist die Zahl der Projekte mit WebAssembly jedoch nicht groß
    • Besonders für Apps, die stark auf das DOM setzen, ist WebAssembly nicht gut geeignet
    • Einer der Hauptgründe ist der Unterschied zwischen den Programmiermodellen von JavaScript und WebAssembly
  • WebAssembly war außerhalb von Sprachen wie C oder Rust bislang kaum erfolgreich

    • Sprachen wie C# haben den Nachteil, dass eine Runtime samt Garbage Collector mitgeliefert werden muss
    • Allerdings werden bald neue WebAssembly-Features eingeführt, die Referenztypen und Garbage Collection unterstützen, wodurch sich die Lage voraussichtlich verbessern wird

Die Fähigkeit des Compilers zur Code-Optimierung ist der Schlüssel zum Erfolg von WebAssembly

  • Damit WebAssembly im Web erfolgreich sein kann, muss der Compiler kleinen und effizienten Code erzeugen können

    • Es ist wichtig, eine kleine Dateigröße im Bereich weniger Kilobyte zu halten
    • Andernfalls bleibt nur, sich auf Hype oder auf eine bestimmte Nutzerbasis zu stützen
  • In der JavaScript-Welt wird die Codegröße bereits mit Bundlern und ähnlichen Tools optimiert

    • Tree-Shaking ist eine Technik, bei der nur die im Programm tatsächlich verwendeten Funktionen und Datentypen einbezogen werden
  • Tree-Shaking ist zwar aus gartenbaulicher wie auch algorithmischer Sicht eine unpassende Metapher, dennoch ist der Begriff weit verbreitet

Der Stand von Tree-Shaking in anderen Sprachen

  • In Sprachen mit schwergewichtiger Runtime wie Go oder Python ist Tree-Shaking bisher noch nicht gut optimiert

    • Selbst das einfachste Go-Programm wird beim Kompilieren zu WebAssembly größer als 2 MB
    • Auch bei Python mit Pyodide müssen Dateien von etwa 20 MB heruntergeladen werden
  • In Server-Umgebungen ist eine große Binärgröße kein großes Problem

    • Für eingeschränkte Umgebungen wie Mobile werden daher gesondert schlanke Toolchains wie MicroPython oder TinyGo entwickelt
  • Sprachimplementierungen für das Web können gar nicht anders, als sich von bestehenden Implementierungen zu unterscheiden

    • Schon die Interaktion mit dem DOM ist eine besondere Umgebung
    • Im Fall von ClojureScript werden die Unterschiede zu Clojure in einer eigenen Dokumentation festgehalten

Diskussion über Tree-Shaking-Algorithmen

  • Der vom Autor entwickelte Hoot-Scheme-Compiler erzeugt derzeit Wasm-Code mit einer Größe von etwa 70 KB

    • Nur Funktionsdefinitionen (Prozeduren) einzubeziehen, ist vergleichsweise einfach
    • Allerdings gibt es einige schwierige Punkte wie die folgenden
  • Im Auswertungsmodell von letrec* sind Bindungen rekursiv und zugleich geordnet, was die Analyse für den Compiler erschwert

    • Bei Record-Typen sorgen vtable-Callbacks dafür, dass viel Code erhalten bleibt
  • Wenn man stark polymorphe Funktionen wie display verwendet, wird viel zusammenhängender Code mit einbezogen

    • Es ist besser, konkrete Funktionen wie write-string zu verwenden
  • Für optimales Tree-Shaking ist Flow Analysis erforderlich

    • Wenn bekannt ist, dass an display kein Bitvektor-Argument übergeben wird, kann zugehöriger Code entfernt werden
  • In Python ist es wegen dynamischem Dispatch und dynamischen Features wie __getattr__ noch schwieriger

    • Auch die Modulstruktur von Python ist ein Faktor, der Tree-Shaking komplizierter macht

Zusammenfassung

  • Durch die Unterstützung von GC wird DOM-Programmierung in WebAssembly auch mit anderen Sprachen als JavaScript möglich
  • Um die Größe der erzeugten Artefakte ausreichend klein zu halten, sind jedoch erhebliche Investitionen in die Toolchains der jeweiligen Sprachen nötig
  • Erforderlich sind die Entwicklung separater Toolchains mit Tree-Shaking-Algorithmen sowie die Optimierung der Standardbibliothek

Meinung von GN⁺

  • Mit der GC-Unterstützung in WebAssembly wird es zwar möglich, im Web verschiedene Sprachen einzusetzen, doch es scheint viele Schwierigkeiten zu geben, bestehende Toolchains unverändert zu übernehmen. Es sieht so aus, als müssten sprachspezifische Implementierungen und Optimierungstechniken entwickelt werden, die auf die Web-Umgebung zugeschnitten sind.

  • Damit Tree-Shaking in dynamisch typisierten Sprachen gut funktioniert, scheint statische Analyse unverzichtbar zu sein. Bei Sprachen wie Python dürfte das wegen der vielen dynamischen Features jedoch nicht einfach sein. Eine Möglichkeit wäre vielleicht sogar, von Anfang an eine neue Sprache zu entwerfen, die für statische Analyse besser geeignet ist.

  • Experimentelle Projekte wie Hoot oder TinyGo könnten gute Referenzen sein. Für den Einsatz in realen Produkten dürfte es aber noch zu früh sein. Vermutlich bleibt nichts anderes übrig, als schrittweise Verbesserungen vorzunehmen.

  • Für Projekte, bei denen Leistung nicht hochsensibel ist und schnelle Entwicklung wichtiger ist, könnte sich etwas wie Pyodide anbieten. Bei Produkten, die großen Wert auf User Experience legen, scheint JavaScript derzeit aber die beste Wahl zu sein.

  • Man könnte auch darüber nachdenken, WebAssembly selbst mit Funktionen wie Tree-Shaking auszustatten. Das wäre jedoch wohl nicht einfach, da die Anforderungen je nach Sprache unterschiedlich sind. Und wenn eine Sprache erscheint, die Tree-Shaking besonders gut unterstützt, könnte es sogar vorteilhafter sein, direkt in dieser Sprache zu programmieren. Es bleibt spannend, wie sich die Rollenverteilung zwischen WebAssembly und Programmiersprachen entwickeln wird.

1 Kommentare

 
GN⁺ 2024-04-15
Hacker-News-Kommentare

Zusammengefasst ergibt sich Folgendes:

  • Im OpenEtG-Projekt wurden folgende Anstrengungen unternommen, um die Größe von in Rust geschriebenen WASM-Binärdateien unter 400 KB zu halten
    • Verwendung von Festkommaarithmetik statt float
    • Verwendung von Vec statt HashMap
    • Minimierung der Nutzung von Strings
    • Verwendung eines kleinen Allocators (talc)
    • Minimierung von Abhängigkeiten (nur rand und fxhash werden verwendet)
    • Vermeidung generischer Vielfalt
    • Entwurf von Algorithmen unter Berücksichtigung der Größe
  • Tree-shaking ist eine falsche Bezeichnung, und im Virgil-Compiler wird dies Reachability Analysis genannt. Während des Kompilierungsprozesses wird vom Einstiegspunkt main aus traversiert, und nur erreichbarer Code wird in die finale Binärdatei aufgenommen.
  • Dank WasmGC können Java und Kotlin kleine WASM-Binärdateien von etwa 2–3 KB erzeugen. Allerdings muss man bei der API-Auswahl vorsichtig sein.
  • DOM-Manipulation mit WASM ist weiterhin von JS abhängig.
  • Der Begriff Tree Shaking entstand, weil Dead Code Elimination schon lange vorher existierte.
  • Das Problem der Codegröße bei WASM entsteht dadurch, dass sowohl das Sprach-Runtime-System als auch die Standardbibliothek mitgebündelt werden müssen.
  • Um dies zu lösen, kann man Shared Libraries und dynamisches Linking in Betracht ziehen.
    • Pyodide ist ein repräsentatives Beispiel für die Unterstützung von dynamischem Linking
    • Wenn der Browser populäre Sprach-Runtimes vorab lädt, könnten Webseiten diese Runtime gemeinsam nutzen
  • Die Sprache Zig eignet sich gut zur Erzeugung kleiner WASM-Binärdateien. Wenn die Größe jedoch unter 100 KB liegt, ist sie kein wichtiger Faktor.
  • Eine eingebaute GC ist nicht für alle Apps wichtig, und es ist besser, Web-Apps ohne GC zu erstellen.
  • Der Erfolgsfaktor von Apps, die WASM verwenden, bleibt weiterhin die Leistungssteigerung.
  • Mit Compile-to-JS-Sprachen wie ClojureScript, TypeScript und ReasonML wurde DOM-Programmierung schon lange in anderen Sprachen als JS betrieben.
  • Über asm.js und emscripten wurden schon vor WASM C-basierte Sprachen für das Web kompiliert und verwendet.
  • Google Maps und Google Earth sind repräsentative Apps, die WASM verwenden.