- Beim Portieren von GC-Sprachen wie Java, Kotlin, Dart, Python und C# nach WebAssembly nutzt WasmGC nicht die Neukompilierung bestehender VMs, sondern die GC-Strukturen, Arrays und das Typsystem von Wasm selbst
- Traditionelle Portierungen auf WasmMVP erleichtern die Wiederverwendung bestehender VMs und Optimierungen, bringen aber die Kosten mit sich, GC oder
malloc/freeim linearen Speicher mitzuführen, sowie Einschränkungen bei Stack-Referenzen, zyklischen Referenzen und Fragmentierung - Mit WasmGC kann die VM Objekte und Speicher direkt verwalten, was die Binärgröße reduziert; im Benchmark
fannkuchlag WasmGC bei 2.3K und damit unter den 6.1~9.6K von C/Rust - Weil WasmGC eine höherstufige Zwischenrepräsentation ist, sind Binaryen
wasm-optund Laufzeitoptimierungen in V8 wichtig; Beispiele zeigen durchschnittlich 1.9× bei Java-Benchmarks und rund 30% Verbesserung bei der Google Sheets Calc Engine - Standardisierung und Browser-Support sind vorangeschritten, aber da nicht einfach eine bestehende VM kompiliert wird, ist neue Toolchain-Arbeit nötig, um Sprachstrukturen auf die primitiven Bausteine von WasmGC abzusenken
Zwei Wege, GC-Sprachen nach Wasm zu bringen
- Das Portieren von GC-Sprachen nach WebAssembly teilt sich grob in zwei Richtungen
- Traditionelles Porting: Die bestehende Sprach-VM wird auf das 2017 veröffentlichte WebAssembly Minimum Viable Product, also WasmMVP, kompiliert
- WasmGC-Porting: Die Sprache wird auf die im GC-Vorschlag definierten GC-Strukturen von Wasm kompiliert
- Traditionelles Porting ähnelt dem Modell, mit dem man eine Sprache auf eine neue CPU-Architektur bringt: Parser, Bibliotheksunterstützung, GC und Optimierer der VM werden gemeinsam genutzt, und es kommt nur ein neues Backend hinzu
- WasmGC-Porting ähnelt eher dem Übertragen einer Sprache nicht auf eine neue Architektur, sondern auf eine neue VM
- Ähnlich wie J2CL, das Java nach JavaScript kompiliert, werden Sprachobjekte als Objekte der Ziel-VM dargestellt und von deren GC verwaltet
- WasmGC ist zwar niedriger angesetzt als JavaScript-VM, JVM oder CLR, aber höherstufig als WasmMVP, weil es VM-verwaltete Strukturen, Arrays und Typbeziehungen bereitstellt
Vorteile und Grenzen traditioneller WasmMVP-Portierungen
- Der größte Vorteil traditioneller Portierungen ist, dass sich bestehender VM-Code, Sprachimplementierung und Optimierungen fast unverändert wiederverwenden lassen
- Ergebnisse auf Basis von WasmMVP verwenden die Grundstrukturen von WasmMVP wie linearen Speicher, Tabellen und Funktionen
- Oft läuft Wasm in VMs, die bereits einen GC besitzen, etwa im Browser, in Node.js, workerd, Deno oder Bun
- Wird in solchen Umgebungen eine GC-Implementierung in das Wasm-Binary eingebettet, führt das zu unnötigem Größenzuwachs
- Auch Sprachen mit linearem Speicher wie C, C++ und Rust müssen bei nennenswerten Allokationen Code für
malloc/freemitliefern dlmallocbenötigt 6K, und selbst das auf geringe Größe ausgelegteemmallocliegt über 1K
- Bei WasmGC verwaltet die VM den Speicher automatisch, sodass weder GC noch
malloc/freein Wasm eingebaut werden müssen- Im
fannkuch-Benchmark aus einem früheren WasmGC-Beitrag lag WasmGC bei 2.3K, C/Rust dagegen bei 6.1~9.6K
- Im
Zyklensammlung, Stack-Referenzen und Fragmentierung
- Im Browser interagiert Wasm häufig mit JavaScript und Web APIs, doch mit WasmMVP und reference types allein ist eine präzise Zyklensammlung bei bidirektionalen Verweisen zwischen Wasm und JS schwierig
- Verweise auf JS-Objekte können nur in einer Wasm-Tabelle abgelegt werden
- Rückverweise von JS nach Wasm können die gesamte Wasm-Instanz als ein großes Objekt referenzieren
- WasmGC definiert Wasm-Objekte, die die VM kennt, und ermöglicht dadurch passende Referenzen zwischen Wasm und JavaScript
- GC-Sprachen müssen auch Referenzen auf dem Stack erkennen, etwa lokale Variablen innerhalb eines Aufruf-Scope
- Bei traditionellen Portierungen kann das Programm wegen des Wasm-Sandboxings seinen eigenen Stack nicht inspizieren
- Als Alternative lässt sich ein Shadow Stack verwenden, oder GC wird nur dann ausgeführt, wenn keine Referenzen auf dem Stack liegen, etwa zwischen Turns der JavaScript-Event-Loop
- Künftige Unterstützung für Stack-Scanning in Wasm könnte traditionelle Portierungen verbessern
- Derzeit erledigt WasmGC die Behandlung von Stack-Referenzen automatisch und ohne Overhead
malloc/freeim linearen Speicher von WasmMVP kann bei lang laufenden Programmen Speicherfragmentierung verursachen- Auch wenn insgesamt genug freier Speicher vorhanden ist, kann eine große Allokation scheitern, wenn kein ausreichend großer zusammenhängender Block existiert
- Fragmentierung kann dazu führen, dass ein Wasm-Modul seinen Speicher häufiger vergrößert, was zu Overhead und Out-of-Memory-Fehlern führen kann
- WasmGC kann Fragmentierung vermeiden, weil die VM Objekte verschieben kann, um den GC-Heap zu komprimieren
Entwicklertools und Sprachsemantik
- Bei traditionellen WasmMVP-Portierungen liegen Objekte als Bytes im linearen Speicher, weshalb Entwicklertools hochstufige Typinformationen nur schwer erkennen können
- Bei WasmGC verwaltet die VM GC-Objekte direkt, was die Integration in Tools erleichtert
- Im Memory-Tab der Chrome DevTools lassen sich Heap-Snapshots von WasmGC-Programmen anzeigen
- Im Beispiel mit einer verketteten Liste erscheinen der Typname
$Nodeund das Feld$next, das auf das nächste Objekt verweist - Auch übliche Heap-Snapshot-Informationen wie Objektanzahl, shallow size und retained size werden bereitgestellt
- Der Debugger in den Chrome DevTools funktioniert ebenfalls mit WasmGC-Objekten
- Traditionelles Porting macht es leicht, die erwartete Sprachsemantik beizubehalten, weil die bestehende VM erneut kompiliert wird
- Beim WasmGC-Porting müssen Sprachstrukturen als neue GC-Typen wie Strukturen und Arrays ausgedrückt werden, weshalb für mehr Effizienz semantische Kompromisse nötig sein können
- Felder in WasmGC-Strukturen haben feste Indizes und Typen; Sprachen, die auf dynamischeren Feldzugriff setzen, können damit Schwierigkeiten haben
- WasmGC besitzt derzeit keine interior pointer; es ist zu erwarten, dass solche Einschränkungen mit der Zeit verbessert werden
- Auch bei anderen Kompilierungen auf eine Ziel-VM gibt es semantische Entscheidungen
- Das Zahlverhalten von dart2js unterscheidet sich von dem der Dart-VM
- Strings in IronPython verhalten sich wie C#-Strings
- Dagegen funktioniert dart2wasm ohne Kompromisse bei der Zahlensemantik, weil Wasm die dafür nötigen Zahltypen effizient darstellen kann
Notwendige Toolchain-Arbeit für WasmGC-Portierungen
- WasmGC-Porting bedeutet nicht, einfach eine bestehende VM neu zu kompilieren
- Einige Teile wie Parserlogik oder AOT-Optimierungen, die nicht direkt mit dem Laufzeit-GC verzahnt sind, lassen sich wiederverwenden
- Im Allgemeinen ist jedoch viel neuer Code nötig, um Sprachstrukturen auf WasmGC-Strukturen und -Arrays abzusenken
- Eine in C geschriebene VM wie die Lua-VM lässt sich zwar in wenigen Minuten nach Wasm kompilieren, doch eine WasmGC-Portierung von Lua muss erst entscheiden und umsetzen, wie sich Lua-Strukturen innerhalb der Einschränkungen des WasmGC-Typsystems ausdrücken lassen
- Der große Nachteil von WasmGC-Portierungen ist der Toolchain-Aufwand
- Der Idealfall wäre, dass das WasmGC-Typsystem alle Sprachen effizient unterstützt und jede Sprache eine WasmGC-Portierung implementiert
- Künftige Erweiterungen des WasmGC-Typsystems könnten beim ersten Teil helfen
- Gemeinsame Toolchain-Arbeit könnte die Last des zweiten Teils senken
Warum WasmGC besser optimierbar sein kann
- WasmGC ist eine höherstufige Zwischenrepräsentation als WasmMVP und bietet daher mehr Optimierungsspielraum
- In einer Beispiel-Funktion werden zweimal GC-Objekte alloziert,
10in ein Feld geschrieben und dann zurückgegeben; logisch ließe sich das zureturn 10vereinfachen- Bei WasmMVP wird eine Allokation zu einem
malloc-Aufruf, undmallocist eine komplexe Funktion mit Nebenwirkungen auf den linearen Speicher; deshalb kann der Optimierer schwer sicher sein, dass die zweite Allokation das Feld des ersten Objekts nicht verändert - Bei WasmGC wird die Allokation als
struct.newdargestellt; das lässt sich als VM-Operation analysieren, sodass Referenzen und Feldwerte verfolgt werden können
- Bei WasmMVP wird eine Allokation zu einem
- WasmGC bietet außerdem explizite Funktionszeiger
ref.func, Aufrufe darüber mitcall_refsowie Typen für Struktur- und Array-Felder - Dass WasmMVP native-nahe Geschwindigkeit erreichen kann, liegt meist daran, dass starke Optimierungscompiler wie LLVM den Großteil der Optimierungen bereits vor der Erzeugung von Wasm ausführen
- LLVM unterstützt WasmGC nicht, und viele GC-Sprachen nutzen LLVM ohnehin nicht, daher braucht WasmGC ein anderes Optimierungsmodell
Optimierung mit Binaryen und wasm-opt
- Bei WasmGC lassen sich allgemeine Optimierungen nach dem Absenken auf Wasm durchführen, sodass mehrere Sprach-Toolchains einen gemeinsamen Wasm-zu-Wasm-Optimierer teilen können
- Das V8-Team investiert in die WasmGC-Unterstützung von Binaryen, dem Optimierungsprojekt für WebAssembly-Toolchains
- Jede Toolchain kann Binaryen über das Kommandozeilenwerkzeug
wasm-optverwenden
- Jede Toolchain kann Binaryen über das Kommandozeilenwerkzeug
- Binaryen besitzt bereits Optimierungen für klassisches WasmMVP wie Inlining, Konstantenpropagierung und Dead Code Elimination; die meisten davon gelten auch für WasmGC
- Wichtige zusätzliche Optimierungen für WasmGC sind unter anderem
- Escape analysis: Verschiebt Heap-Allokationen in Locals
- Devirtualization: Wandelt indirekte Aufrufe in direkte Aufrufe um
- Stärkere globale Dead Code Elimination
- Whole-program type-aware content flow analysis, GUFA
- Cast optimizations: Entfernen redundanter Casts und Verschieben von Cast-Positionen
- Type pruning
- Type merging
- Typverfeinerung für Locals, Globals, Felder und Signaturen
- Die neuen GC-Optimierungen und ihre Nutzung in Binaryen sind in den Binaryen docs zusammengefasst
- Bei Leistungsmessungen von Java auf Basis der J2Wasm-Ausgabe beschleunigte
wasm-optjeden Benchmark und brachte im Schnitt 1.9× mehr Geschwindigkeit
WasmGC-Optimierungen in V8
- GC-Sprachen haben ein anderes Performancemodell als Sprachen wie C, C++ und Rust, die stark auf Inlining zur Compile-Zeit setzen
- GC-Sprachen wie Java und Dart verlassen sich meist darauf, dass die VM Inlining und Optimierungen zur Laufzeit durchführt
- In Java beginnt jeder Aufruf zunächst als indirekter Aufruf
- Kindklassen können Funktionen der Elternklasse überschreiben, und das gilt auch, wenn über eine Referenz auf den Elterntyp ein Kind aufgerufen wird
- Wenn die Toolchain indirekte Aufrufe in direkte umwandeln kann, ist das vorteilhaft, aber in realem Java-Code gibt es viele Pfade, die sich statisch nur schwer als direkte Aufrufe ableiten lassen
- V8 hat für WasmGC speculative inlining implementiert
- Indirekte Aufrufe werden zur Laufzeit beobachtet
- Wenn eine bestimmte Aufrufstelle ein einfaches Muster mit wenigen Zielen zeigt, wird sie zusammen mit Guard-Checks inlined
- Die Google Sheets Calc Engine ist eine Java-Codebasis zur Auswertung von Tabellenkalkulationsformeln und wurde zuvor mit J2CL nach JavaScript kompiliert
- Das V8-Team arbeitete mit Sheets und J2CL zusammen, um diesen Code nach WasmGC zu portieren
- In diesem Code hatte speculative inlining den größten Effekt unter allen einzelnen in V8 für WasmGC implementierten Optimierungen
- Der Geschwindigkeitsgewinn lag bei etwa 30% und war größer als die gesamte übrige messbare Optimierung zusammen
- Weitere WasmGC-Optimierungen in V8 umfassen load elimination, type-based optimizations, branch elimination, constant folding, escape analysis und common subexpression elimination
- Die Typinformationen von WasmGC werden auch für Laufzeitoptimierungen verwendet
- Wenn
ref.testeine bestimmte Typprüfung besteht, sollte einref.castauf denselben Typ ebenfalls erfolgreich sein - Dadurch lassen sich Muster wie Downcasts nach einem Java-
instanceofoptimieren, indem der Cast entfernt wird
- Wenn
- Bei WasmMVP war die Aufgabenteilung zwischen Toolchain- und VM-Optimierungen relativ klar; bei WasmGC können sich beide stärker überschneiden, weil GC-Sprachen eigene Eigenschaften mitbringen und die WasmGC-Darstellung zusätzliche Optimierungsmöglichkeiten bietet
Aktueller Stand und Einstiegspunkte
- WasmGC hat bei der W3C Phase 4 erreicht und ist damit ein vollständiger und finalisierter Standard
- Chrome 119 enthält Unterstützung für WasmGC
- Firefox 120 wurde für Ende desselben Monats mit WasmGC-Unterstützung erwartet
- In der Flutter demo treibt nach WasmGC kompiliertes Dart die Anwendungslogik an, einschließlich Widgets, Layout und Animationen
- Folgende Toolchains unterstützen WasmGC
- Der kleine Beispiel-Quellcode aus dem Abschnitt zu Entwicklertools ist ein handgeschriebenes „hello world“-Beispiel für ein WasmGC-Programm
- Dort lassen sich die Typdefinition
$Nodeund die Erzeugung mitstruct.newsehen
- Dort lassen sich die Typdefinition
- Das Binaryen-Wiki behandelt, wie Compiler WasmGC-Code ausgeben können, der sich gut optimieren lässt
- GC Implementation - Lowering Tips
- Außerdem lassen sich die von den Toolchains für Java, Dart und Kotlin verwendeten Binaryen-Passes und Flags nachschlagen
1 Kommentare
Meinungen auf Hacker News
WASM wirkt wie ein Beispiel für eine Thin Waist; wenn noch ein Garbage Collector hinzukommt, entsteht keine N×M-, sondern eine N+M-Struktur. Also N Sprachen + M virtuelle Maschinen + G Garbage Collectors, und V8 hat bereits einen ausgereiften Garbage Collector.
Ich fragte mich, ob es ein Tool gibt, das von WASM zur JVM führt, und auf GitHub gab es eines. Ich habe es nicht selbst ausprobiert, sondern danach gesucht, weil auch die JVM ausgereifte, parallele Garbage Collectors hat.
Da ich nicht erwartet hatte, dass WASMGC so schnell kommt, freue ich mich jetzt auch auf WASM Threads für echte Parallelität, nicht nur I/O-Parallelität.
Wenn sich die Chance ergibt, Asynchronität, Parallelität und Garbage Collection effektiv zu lösen, kann WASM noch stärker werden und muss für Entwickler keine Quelle von Verwirrung oder Schwierigkeiten sein. WASI ist meiner Ansicht nach auch deshalb wichtig, weil es die Chance bietet, eine API zu definieren, die so stabil ist wie POSIX.
1: https://www.oilshell.org/blog/2022/02/diagrams.html
2: https://github.com/cretz/asmble
Ich hoffe, dass die Ideen und Erklärungen dazu, wie die WASM-Zwischendarstellung auf JVM-Bytecode gemappt wurde, jemandem helfen, der eine offiziellere Implementierung baut. Aktuell gibt es keine Pläne für WASM-GC-Unterstützung.
„Mehr als 20 Anbieter von Programmierwerkzeugen stellen rund 26 Programmiersprachen für .NET bereit, darunter C++, Perl, Python, Java, COBOL, RPG und Haskell.“
Quelle: https://news.microsoft.com/2001/10/22/massive-industry-and-d...
Nicht so sehr, weil die Wissenschaft bei der JVM besonders wagemutig gewesen wäre, sondern weil JVM-Programme weit verbreitet waren und häufig auf eine Weise genutzt wurden, die einzelne Garbage-Collection-Ansätze unter Druck setzte.
[^1]: https://www.baeldung.com/jvm-garbage-collectors. Die Azul-JVM hat ebenfalls einen eigenen, anderen Garbage Collector. Das sind nur die, die ich kenne; es dürfte einiges geben, das ich ausgelassen habe.
Vermutlich war es im Vergleich zu Koroutinen gemeint, aber Koroutinen sind sequenziell. Auch wenn die Ausführungsreihenfolge beliebig sein kann, kann man sich darauf verlassen, dass sie nicht gleichzeitig laufen.
Wenn wasm Threads übernimmt, wirkt das wie ein weiterer bedauerlicher „Worse is Better“-Fall. Threads gehören zu den schlechtesten Nebenläufigkeitsmodellen, die wir entwickelt haben. Abgesehen natürlich von Dingen wie concurrent COMEFROM.
Julia-WASM-Tools können Apps erstellen, die diese Funktion unterstützen oder benötigen. Es gibt zum Beispiel ein Beispiel, in dem ein ODE-Solver nach WASM kompiliert wird: https://tshort.github.io/WebAssemblyCompiler.jl/stable/examp...
Damit es direkt funktioniert, braucht man Chrome v119, weil diese Version erstmals die Garbage-Collection-Unterstützung aktiviert hat. Weitere Details gibt es auf der Startseite des WASM-Compilers: https://tshort.github.io/WebAssemblyCompiler.jl/stable/
Ich hoffe, dass sich dank des neuen Typs
Memory{T}mehr Code kompilieren lässt.Es fühlt sich ein bisschen an wie „WASM ist das neue LLVM“. Ich frage mich, ob andere das ähnlich empfinden.
Ich verstehe, was man damit erreichen will, und habe auch Demos gesehen, die seine Stärke zeigen, aber größtenteils ist es technisch immer noch sehr Low-Level, und wenn man es tatsächlich einsetzen will, wird es oft ziemlich umständlich.
Ich frage mich, wann es wohl bereit sein wird, so breit angenommen zu werden, dass man es statt der aktuellen Ansätze als Ziel wählt.
Der Name führt häufig zu diesem Missverständnis: LLVM war anfangs als virtuelle Maschine gedacht, war es aber tatsächlich nie und ist es auch heute nicht. Schon der erste Satz der Homepage macht das klar: https://llvm.org/
Im Grunde ist es ein Bündel von C++-Bibliotheken, die eine Intermediate Representation implementieren, die sich im Lauf der Zeit ändert, und ein Werkzeug, das beim Schreiben von Compilern hilft.
Vor etwa zehn Jahren gab es bei Google ein Projekt, das auf einen ähnlichen Bereich wie WASM zielte, und ich glaube, dort unterlag man diesem Missverständnis. Man hielt LLVM für eine virtuelle Maschine. Vermutlich war es etwas wie PNaCl.
Es ist ein wenig ähnlich wie bei LuaJIT, das Lua auf Lua 5.1 festgelegt hat. Lua war kein Standard, aber für eine Neuimplementierung kann man eine bestimmte Version einfrieren. Dieser Ansatz hat allerdings ein offensichtliches Problem: Diejenigen, die neu implementieren, kennen nicht alle Bugs, die sie damit ebenfalls in der Zeit einfrieren.
Bei manchen „Kompromissen“ von WASM habe ich zwar die Augenbrauen hochgezogen, aber eines ist unbestreitbar: WASM ist tatsächlich eine virtuelle Maschine.
Ich habe den Vortrag zu WASM GC gesehen, und die Entwickler haben offen über ihre Kompromisse gesprochen. Zum Beispiel, dass anfangs Runtime-Casts nötig sind und der gemessene Overhead in einem vernünftigen Rahmen liegt. Genau diese Haltung schafft eher Vertrauen.
https://old.reddit.com/r/ProgrammingLanguages/comments/17crk...
Ehrlich gesagt verstehe ich wasm nicht so recht. Mir ist nicht klar, welches Problem wir genau lösen wollen.
Ich frage mich, ob das Ziel darin besteht, Anwendungen in Sprachen zu schreiben, die weder JavaScript sind noch nach JavaScript transpiliert werden. Ich weiß nicht, ob das auch den Teil umfasst, der mit der Browser-DOM-Darstellungsschicht interagiert. Nach meinem Verständnis kann man das in der Praxis nicht wirklich tun; falls das stimmt, frage ich mich, ob das Ziel dann darin besteht, Anwendungen wie bei Flash in einem dedizierten Canvas innerhalb des DOM auszuführen. Wenn ja, weiß ich nicht, wie groß diese Nische ist.
In einem anderen Thread war auch davon die Rede, wasm für Cloudflare Workers zu verwenden. Vermutlich, weil das über einen JavaScript-Interpreter läuft. Aber wenn Leute Cloudflare Workers mit beliebigen Runtimes schreiben wollen, könnte Cloudflare doch einfach Unterstützung für andere Runtimes hinzufügen, oder? Diese Fähigkeit dürften sie haben.
Oder ich frage mich, ob wasm abseits der Verbindung zum JavaScript-Ökosystem als Bytecode eigenständig besonders gut ist.
Ich habe das Gefühl, mir fehlt eine Einführung, die erklärt, warum diese Technik interessant ist, aber zu diesem Zeitpunkt ist es mir etwas peinlich zu fragen.
LLVM bietet ohne erheblichen Programmieraufwand keine „von Haus aus einfache Security-Sandbox“. wasmer hat zwar ein LLVM-Backend, aber persönlich sehe ich genau darin das größte Unterscheidungsmerkmal von WASM.
Es bewegt sich zwar in eine ähnliche Richtung wie LLVM, ist davon aber noch weit entfernt.
Egal wie großartig es ist und wie viele Möglichkeiten es eröffnet: Ich muss immer wieder daran denken, dass Browser heute extrem komplex sind und die Einstiegshürde, selbst einen zu bauen, fast schon absurd steil ist.
Ich weiß nicht, warum die erfolgreichste und zugänglichste Hypermedia-Plattform der Welt leicht implementierbar sein müsste. Natürlich ist das erfolgreichste Online-System der Welt komplex und funktionsreich. Deshalb nutzen wir es, und deshalb hat es gewonnen.
Dass das am Ende tatsächlich gebaut und veröffentlicht wurde, ist beeindruckend. Von dem Plan, GC in WASM einzubauen, hatte man jahrelang gehört, und ich war mir nicht sicher, ob das wirklich passieren würde.
Ich frage mich, wie sehr es Sprachen hilft, die auf WASM abzielen und bei denen es das Problem gibt, dass die Binärdateien groß werden, weil sie die Runtime mitbringen müssen. Soweit ich mich erinnere, brauchte Blazor selbst für Hello World etwa 1 MB; ich weiß nicht, ob WasmGC hier hilft.
Was mir natürlich Sorgen macht: Wenn es um Funktionen geht, die nur .NET braucht, ist der Anreiz, sie hinzuzufügen, nicht besonders groß. An diesem Punkt könnte eine Art
include-Funktion, mit der eine bestimmte GC-Version gecacht und von anderen WASM-Assemblies geladen werden kann, trotz der Schmerzen nützlicher sein.[0] - https://github.com/WebAssembly/gc/issues/77
C und Rust enthalten zwar keinen Garbage Collector, müssen aber für das Speichermanagement trotzdem malloc/free mit einbinden. Java ist hier kleiner, weil es überhaupt keinen Speichermanagement-Code mitbündeln muss.
https://developer.chrome.com/blog/wasmgc/
Bei Blazor wird es wohl nur beim GC helfen. Soweit ich mich erinnere, muss Blazor die gesamte dotnet-Runtime mit ausliefern.
Kotlins neue wasm-Unterstützung ist ziemlich spannend. Es gibt eine experimentelle Version von Compose Multiplatform, die den Browser als Ziel haben kann, und sie soll WASM nutzen.
Compose Multiplatform ist im Grunde Googles Jetpack Compose für Android plus Unterstützung für weitere Plattformen.
Stand vor ein paar Tagen ist die iOS-Unterstützung Alpha und soll nächstes Jahr Beta werden. Android- und Desktop-Unterstützung sind inzwischen stabil. Wenn diese stabil sind, kann man praktisch UI-Anwendungen schreiben, die auf jeder Plattform laufen.
Der wasm-Compiler soll mit dem nächsten großen Kotlin-Release, Kotlin 2.0, erscheinen; darin ist auch der neue Compiler k2 enthalten. Anfang nächsten Jahres scheint realistisch. k2 ist derzeit als Beta verfügbar und kann in Kotlin 1.9.x aktiviert werden.
Der Vorteil des Kotlin-Multiplattform-Ökosystems ist, dass es bereits viele Bibliotheken gibt, die auf mehreren Plattformen laufen. Daher wird der wasm-Compiler schnell Teil davon werden und gute Bibliotheken übernehmen. Im Großen und Ganzen muss man die Build-Konfiguration auf dieses Ökosystem ausrichten und fehlendes plattformspezifisches Verhalten implementieren.
Ein weiterer interessanter Punkt in diesem Bereich ist, Bibliotheken zu verwenden und zu verknüpfen, die in unterschiedlichen Sprachen geschrieben sind. Viele plattformspezifische Teile werden zum Beispiel wahrscheinlich auf bereits vorhandene C- oder Rust-Bibliotheken setzen. In vielen Fällen könnten es dieselben Bibliotheken sein, die Kotlin Native verwendet.
Ich frage mich, ob auf Desktop, Web, iOS und Android dieselben Widgets verwendet werden. Bei Android verstehe ich, dass es nativ ist. Ich weiß nicht, ob es auf anderen Plattformen andere Stile nachahmt. Die Kernfrage ist, wie sich eine Compose-Multiplatform-App auf Nicht-Android-Plattformen anfühlt.
Dart im Web fühlt sich für mich zum Beispiel sehr ruckelig an, und Electron auf dem Desktop ebenfalls. Dart auf dem Desktop ist okay, aber die Apps haben meist große Buttons und Abstände und fühlen sich eher wie Android-Apps in einem Emulator an als wie echte native Apps.
Ich frage mich auch, ob man Kotlin als Sprache für neue Projekte empfehlen kann.
Ich frage mich, ob jemand erklären kann, warum dieser Blogpost und die Chrome-Ankündigung Go nicht erwähnen.
Go nutzt ebenfalls Garbage Collection, was den Eindruck erweckt, dass es von dieser Änderung nicht profitieren kann.
Ich frage mich, ob es sinnvoll wäre, wenn die Runtime eine eingebaute Allokationsbibliothek bereitstellt, die C/C++/Rust nutzen können
Programme könnten diese Bibliothek wählen, statt ihre eigene Allokationsbibliothek mitzubündeln
Die primitiven Funktionen von Wasm GC wurden so entworfen, dass mit möglichst kleinen Ergänzungen die größte Verbesserung erreicht wird. Auch wenn andere primitive Funktionen wie SIMD hinzugefügt werden, dann deshalb, weil sie die „ungefähr minimalen primitiven Funktionen“ sind, die für diese Fähigkeit nötig sind. Hier braucht man möglichst kleine Bausteine
Eine Speicherallokator-API wie malloc/free ist jedoch keine minimale Funktion. Ein Speicherallokator ist in Wirklichkeit ein Bündel aus Richtlinien und Designentscheidungen, und der Algorithmus ergibt sich daraus. Der grundlegende Baustein in diesem Bereich ist ein „vom Allokator besessenes Stück linearer Speicher“, und genau diesen linearen Speicher hatte WASM schon vom ersten Tag an
Es kann sinnvoll sein, dass einzelne Runtimes so etwas anbieten. Zum Beispiel, indem man einem eingebauten wasm-Import einen Teil des linearen Speichers übergibt und ihn von einem in der Runtime implementierten Allokator verwalten lässt. Ich halte es aber für sehr unwahrscheinlich, dass das standardisiert wird. Außerdem sind die meisten Werkzeuge für Wasm-Sprachen ohne GC darauf ausgelegt, bestehende Codebasen samt deren Allokatoren zu portieren. Am Ende hätte man also mehr Code zu warten für eine einmalige Implementierung, was den Aufwand möglicherweise nicht wert ist
Wenn es nicht deterministisch ist, können Unterschiede im Programmverhalten relevant werden. Wenn etwa ein Browser einen „intelligenten“ Allokator einbaut, der Speicher besser wiederverwendet, könnte ein Programm, das in anderen Browsern wegen Fragmentierung des linearen Speichers mit Speichermangel scheitert, in diesem Browser erfolgreich laufen. Browser haben historisch sehr viel Aufwand betrieben, um solche Unterschiede durch Standardisierung des Verhaltens zu vermeiden
Auch das Verhalten einer malloc/free-Implementierung zu standardisieren, ist aus mehreren Gründen schwierig. Erstens gibt es viel Komplexität, etwa wie die free list behandelt wird oder welche Chunk-Größen verwendet werden. Das ist zu viel für eine Spezifikation. Zweitens ließe sich ein einmal standardisiertes Verhalten nicht mehr verbessern, obwohl es auch heute noch bessere Ideen für malloc/free gibt
Aus diesen Gründen bin ich skeptisch, dass so etwas in die wasm-Spezifikation aufgenommen wird
GC vermeidet diese Probleme dagegen. Da Pointer-Werte nicht beobachtbar sind, gibt es nichts zu spezifizieren
allocund verwandte Funktionen als Runtime-Funktionen hinzufügen, damit traditioneller Code sie aufruft, und dann beliebige GCs darauf aufbauenIch bin dieser Richtung gegenüber etwas skeptisch. Sie erhöht die Komplexität von WebAssembly erheblich.
Garbage Collectors sind leaky Abstractions. Manche unterstützen interne Pointer, manche nicht. Manche unterstützen parallele Arbeit mit gemeinsam genutztem Speicher, manche nicht. Manche benötigen Kompaktierung, wodurch C-FFI schwieriger wird, manche nicht. Manche müssen tief in Mechanismen für Green Processes/Threads und erweiterbare Stacks integriert sein, manche nicht.
Wenn man sich Sprachen wie Erlang, JavaScript, Python und Go ansieht, spiegeln sich Entscheidungen auf Sprachebene bis zu einem gewissen Grad im Garbage Collector wider.
Die Idee einer universellen/allgemeinen virtuellen Maschine, die mehrere Sprachen unterstützt, wurde mit JVM, CLR, Parrot usw. mehrfach versucht und war nur begrenzt erfolgreich. Ich frage mich, was diesmal anders ist.
JVM/CLR haben zuerst eine virtuelle Maschine, eine Spezifikation für Intermediate-Language-Instruktionen und eine Runtime gebaut und sie dann möglichst breit verteilt, in der Hoffnung auf allgemeine Client-Akzeptanz. Anders gesagt: Man erwartete, dass eine „Intermediate-Language-VM“ so attraktiv sei, dass sie sich in der gesamten Branche verbreiten würde. Diese Erwartung traf nur teilweise zu. JVM/CLR verbreiteten sich auf Desktop und Servern, aber in Webbrowsern scheiterten Java Applets und Microsoft Silverlight, und auf mobilen Plattformen wurden sie ebenfalls nicht breit übernommen.
Bei WASM ist die Reihenfolge umgekehrt. Es geht von etwas aus, das bereits branchenweit verteilt und akzeptiert ist, nämlich JavaScript, und baut von dort aus rückwärts eine „virtuelle Maschine, eine Spezifikation für Intermediate-Language-Instruktionen und eine Runtime“.
Aus dieser Perspektive war die sogenannte „Spielzeugsprache“ JavaScript ein 20 Jahre langes trojanisches Pferd, um zuerst auf allen Clients breit verteilt zu werden. Jetzt sagt die Branche gewissermaßen: „Hat jemand bemerkt, dass wir mit JavaScript bereits eine universelle Runtime für Server + Desktop + Browser + Mobile haben? Bauen wir eine Intermediate-Language-Runtime und machen sie schnell.“
Es gab auch einige technische Probleme. Die Sun-JVM hatte keine rohen Pointer und war daher schwerlich ein performantes Ziel für pointerbasierte Sprachen wie C/C++; die MS-CLR war unter macOS nicht verfügbar. Die minimale CLR von Silverlight ist eine Ausnahme. Aber mehr als diese technischen Grenzen erklärt die zeitliche Reihenfolge, in der JavaScript wie ein harmloses trojanisches Pferd verteilt wurde, die Situation besser.
Wasm ist niedriger angesetzt als die meisten der oben genannten Bytecode-Formate. Das gilt selbst mit Wasm GC, das statisch typisierte Structs und Arrays hinzufügt, und dem function-references-Vorschlag, der typisierte Funktionen einführt. Wasm GC hat außerdem explizite Unterstützung für den tagged Pointer
i31ref.Aus diesen Gründen ist Wasm GC gerade dadurch, dass es niedriger angesetzt ist, allgemeiner als die oben genannten Versuche.
Eine „universelle“ oder „allgemeine“ VM zu schaffen, die mehrere Sprachen gleichwertig und nahtlos unterstützt, ist kein ausdrückliches Ziel von WASM-GC. Man geht davon aus, dass jede Implementierung auf der grundlegenden Unterstützung, die WASM bietet, eigene Hacks und spezielle Semantik benötigen kann; FFI und Interoperabilität zwischen Sprachen werden als völlig separates Problem betrachtet, und das ist in Ordnung.
JavaScript-Garbage-Collection wird bleiben, und von den genannten Beispielen scheint sie die hartnäckigste und am festesten etablierte zu sein.
In vielen Fällen müssen sich andere Sprachen und Anwendungen/Bibliotheken daran anpassen, selbst wenn das unterlegen ist.
wasm stellt GC-Funktionen bereit, damit Sprachen mit Managed-Memory-Runtime auf wasm-Umgebungen abzielen können, ohne unter den Grenzen eines restriktiven Speichermodells zu leiden, und nebenbei, um die Bundle-Größe zu reduzieren.
wasm könnte künftig besser konfigurierbare GC-Parameter unterstützen, damit sie besser zu den Eigenschaften der Gastsprache passen. Außerdem haben Sprachimplementierer, anders als bei den zum Vergleich herangezogenen allgemeinen Sprach-Runtimes, gar nicht die Option, eine vollständig maßgeschneiderte Runtime zu bauen.
Mehrere Bedenken zur derzeitigen Umsetzbarkeit von WASM GC werden hier behandelt. Englische Google-Übersetzung:
https://habr-com.translate.goog/ru/articles/757182/?_x_tr_sl...
Original:
https://habr.com/ru/articles/757182/
Der Beitrag stammt vom Autor von TeaVM, der seit 10 Jahren daran arbeitet, Java- und JVM-Code effizient im Browser auszuführen. https://teavm.org/
Die bestehende Java-zu-JavaScript-Übersetzung von TeaVM nutzt zwar den JS-GC des Browsers, ist aber dennoch performant. Es wird interessant sein zu beobachten, ob WASM GC mit zunehmender Reife noch schneller werden kann.
Ein paar Anmerkungen zu den im Beitrag behandelten Problemen: Das Problem, dass ein manueller Shadow Stack nötig ist, wird durch WasmGC gelöst. Wie im Link beschrieben, entspricht das der Funktionsweise in JS.
Das Fehlen von try-catch wird durch den Wasm-Exception-Handling-Vorschlag gelöst, der bereits in Browsern ausgeliefert wurde: https://github.com/WebAssembly/exception-handling/blob/main/...
Auch Null-Prüfungen werden größtenteils durch WasmGC gelöst. Die Spezifikation definiert nicht-nullbare lokale Typen, und die VM kann sie mit Techniken wie den im Beitrag erwähnten, etwa über Signale, optimieren. Wizard macht das zum Beispiel so.
Klasseninitialisierung ist, wie im Beitrag gesagt, ein schwieriges Problem. J2Wasm und Binaryen arbeiten daran, dies durch statische Analyse auf Toolchain-Ebene zu optimieren. Ein kürzlich von mir erstellter PR hat in diese Richtung Fortschritte gebracht: https://github.com/WebAssembly/binaryen/pull/6061
Der im Beitrag erwähnte vtable-Overhead kann ein Problem sein. Gute Messwerte kenne ich allerdings noch nicht. Es gibt einige Ideen für Post-MVP-Lösungen für Method Dispatch, aber noch nichts Konkretes.
Zu Null-Prüfungen und Traps gab es Diskussionen über Varianten von GC-Instruktionen, die statt eines Traps eine Exception werfen. Da Messungen bislang jedoch kein großes Problem zeigen, hat das niedrige Priorität.
Der Autor liegt richtig damit, dass Stack Walking, Signale und Speicherkontrolle wichtige Bereiche sind.
Insgesamt sind wir dank WasmGC und Exception Handling bei Java, wie es J2Wasm heute ausgibt, in einer ziemlich guten Lage. Es ist normalerweise schneller als J2CL, das Java nach JavaScript kompiliert. Trotzdem gibt es eindeutig noch Raum für Verbesserungen.