WebAssembly Garbage Collection (WasmGC) is nu standaard ingeschakeld in Chrome

Er zijn twee soorten programmeertalen: programmeertalen die door afval worden verzameld en programmeertalen die handmatig geheugenbeheer vereisen. Voorbeelden van de eerste zijn, naast nog veel meer, Kotlin, PHP of Java. Voorbeelden van dit laatste zijn C, C++ of Rust. Over het algemeen is het waarschijnlijker dat programmeertalen op een hoger niveau standaard garbagecollection hebben. In deze blogpost ligt de nadruk op zulke vervuilde programmeertalen en hoe ze kunnen worden gecompileerd naar WebAssembly (Wasm). Maar wat is afvalinzameling (vaak GC genoemd) om mee te beginnen?

Browserondersteuning

  • Chroom: 119.
  • Rand: 119.
  • Firefox: 120.
  • Safari: 18.2.

Afvalinzameling

In vereenvoudigde bewoordingen is het idee van garbage collection de poging om geheugen terug te winnen dat door het programma is toegewezen, maar waarnaar niet langer wordt verwezen. Een dergelijk geheugen wordt afval genoemd. Er zijn veel strategieën voor het implementeren van afvalinzameling. Een daarvan is het tellen van referenties , waarbij het doel is om het aantal verwijzingen naar objecten in het geheugen te tellen. Wanneer er geen verwijzingen meer zijn naar een object, kan het worden gemarkeerd als niet langer gebruikt en is het dus klaar voor garbagecollection. PHP 's garbage collector maakt gebruik van referentietelling , en met behulp van de functie xdebug_debug_zval() van de Xdebug- extensie kun je onder de motorkap kijken. Beschouw het volgende PHP-programma.

<?php
  $a= (string) rand();
  $c = $b = $a;
  $b = 42;
  unset($c);
  $a = null;
?>

Het programma wijst een willekeurig getal dat aan een string is toegewezen, toe aan een nieuwe variabele genaamd a Vervolgens worden er twee nieuwe variabelen gemaakt, b en c , en wordt daaraan de waarde a toegekend. Daarna wijst het b opnieuw toe aan het getal 42 en schakelt vervolgens c uit. Ten slotte wordt de waarde van a ingesteld op null . Door elke stap van het programma te annoteren met xdebug_debug_zval() , kun je de referentieteller van de garbage collector aan het werk zien.

<?php
  $a= (string) rand();
  $c = $b = $a;
  xdebug_debug_zval('a');
  $b = 42;
  xdebug_debug_zval('a');
  unset($c);
  xdebug_debug_zval('a');
  $a = null;
  xdebug_debug_zval('a');
?>

Het bovenstaande voorbeeld levert de volgende logboeken op, waarin u ziet hoe het aantal verwijzingen naar de waarde van de variabele a na elke stap afneemt, wat logisch is gezien de codereeks. (Je willekeurige nummer zal natuurlijk anders zijn.)

a:
(refcount=3, is_ref=0)string '419796578' (length=9)
a:
(refcount=2, is_ref=0)string '419796578' (length=9)
a:
(refcount=1, is_ref=0)string '419796578' (length=9)
a:
(refcount=0, is_ref=0)null

Er zijn nog andere uitdagingen bij het verzamelen van afval, zoals het detecteren van cycli , maar voor dit artikel is een basiskennis van het tellen van referenties voldoende.

Programmeertalen worden geïmplementeerd in andere programmeertalen

Het voelt misschien als een begin, maar programmeertalen worden in andere programmeertalen geïmplementeerd. De PHP-runtime wordt bijvoorbeeld voornamelijk in C geïmplementeerd. Je kunt de PHP-broncode bekijken op GitHub . De garbage collection-code van PHP bevindt zich voornamelijk in het bestand zend_gc.c . De meeste ontwikkelaars installeren PHP via de pakketbeheerder van hun besturingssysteem. Maar ontwikkelaars kunnen ook PHP bouwen vanuit de broncode . In een Linux-omgeving zouden de stappen ./buildconf && ./configure && make bijvoorbeeld PHP bouwen voor de Linux-runtime. Maar dit betekent ook dat de PHP-runtime kan worden gecompileerd voor andere runtimes, zoals, je raadt het al, Wasm.

Traditionele methoden voor het porten van talen naar de Wasm-runtime

Onafhankelijk van het platform waar PHP op draait, worden PHP-scripts in dezelfde bytecode gecompileerd en door de Zend Engine uitgevoerd. De Zend Engine is een compiler en runtime-omgeving voor de PHP-scripttaal. Het bestaat uit de Zend Virtual Machine (VM), die is samengesteld uit de Zend Compiler en de Zend Executor. Talen zoals PHP die zijn geïmplementeerd in andere talen op hoog niveau, zoals C, hebben doorgaans optimalisaties die zich richten op specifieke architecturen, zoals Intel of ARM, en vereisen voor elke architectuur een andere backend. In deze context vertegenwoordigt Wasm een ​​nieuwe architectuur. Als de VM architectuurspecifieke code heeft, zoals just-in-time (JIT) of ahead-of-time (AOT) compilatie, implementeert de ontwikkelaar ook een backend voor JIT/AOT voor de nieuwe architectuur. Deze aanpak is heel logisch omdat het grootste deel van de codebase vaak gewoon opnieuw kan worden gecompileerd voor elke nieuwe architectuur.

Gezien het lage niveau van Wasm, is het normaal om daar dezelfde aanpak te proberen: compileer de belangrijkste VM-code opnieuw met zijn parser, bibliotheekondersteuning, garbage collection en optimizer voor Wasm, en implementeer indien nodig een JIT- of AOT-backend voor Wasm. Dit is mogelijk sinds de Wasm MVP en werkt in veel gevallen in de praktijk goed. In feite is PHP gecompileerd naar Wasm de drijvende kracht achter de WordPress Playground . Lees meer over het project in het artikel Bouw WordPress-ervaringen in de browser met WordPress Playground en WebAssembly .

PHP Wasm draait echter in de browser in de context van de hosttaal JavaScript. In Chrome worden JavaScript en Wasm uitgevoerd in V8 , de open source JavaScript-engine van Google die ECMAScript implementeert zoals gespecificeerd in ECMA-262 . En V8 heeft al een garbage collector . Dit betekent dat ontwikkelaars die bijvoorbeeld gebruik maken van PHP gecompileerd met Wasm, uiteindelijk een garbage collector-implementatie van de geporteerde taal (PHP) naar de browser sturen die al een garbage collector heeft, wat net zo verspillend is als het klinkt. Dit is waar WasmGC in beeld komt.

Het andere probleem van de oude aanpak om Wasm-modules hun eigen GC te laten bouwen bovenop het lineaire geheugen van Wasm is dat er dan geen interactie is tussen Wasm's eigen garbage collector en de ingebouwde garbage collector van de naar Wasm gecompileerde taal. wat vaak problemen veroorzaakt zoals geheugenlekken en inefficiënte verzamelpogingen. Door Wasm-modules de bestaande ingebouwde GC te laten hergebruiken, worden deze problemen vermeden.

Programmeertalen overzetten naar nieuwe runtimes met WasmGC

WasmGC is een voorstel van de WebAssembly Community Group . De huidige Wasm MVP-implementatie kan alleen omgaan met getallen, dat wil zeggen gehele getallen en floats, in lineair geheugen, en nu het voorstel voor referentietypen wordt verzonden, kan Wasm bovendien externe referenties vasthouden. WasmGC voegt nu struct- en array-heaptypen toe, wat ondersteuning betekent voor niet-lineaire geheugentoewijzing. Elk WasmGC-object heeft een vast type en een vaste structuur, waardoor VM's eenvoudig efficiënte code kunnen genereren om toegang te krijgen tot hun velden, zonder het risico van deoptimalisaties die dynamische talen zoals JavaScript met zich meebrengen. Dit voorstel voegt daarmee efficiënte ondersteuning voor beheerde talen op hoog niveau toe aan WebAssembly, via struct- en array-heap-typen waarmee taalcompilers die zich op Wasm richten, kunnen integreren met een garbage collector in de host-VM. In vereenvoudigde bewoordingen betekent dit dat met WasmGC het porten van een programmeertaal naar Wasm betekent dat de garbage collector van de programmeertaal niet langer deel hoeft uit te maken van de port, maar dat in plaats daarvan de bestaande garbage collector kan worden gebruikt.

Om de impact van deze verbetering in de echte wereld te verifiëren, heeft het Wasm-team van Chrome versies van de Fannkuch-benchmark (die datastructuren toewijst terwijl deze werken) samengesteld uit C , Rust en Java . De binaire bestanden C en Rust kunnen tussen de 6,1 K en 9,6 K groot zijn, afhankelijk van de verschillende compilervlaggen, terwijl de Java-versie met slechts 2,3 K veel kleiner is! C en Rust bevatten geen garbage collector, maar ze bundelen nog steeds malloc/free om geheugen te beheren, en de reden dat Java hier kleiner is, is omdat het helemaal geen geheugenbeheercode hoeft te bundelen. Dit is slechts één specifiek voorbeeld, maar het laat zien dat WasmGC-binaire bestanden het potentieel hebben om erg klein te zijn, en dit is nog vóór enig significant werk aan het optimaliseren van de grootte.

Een door WasmGC geporteerde programmeertaal in actie zien

Kotlin Wasm

Een van de eerste programmeertalen die dankzij WasmGC naar Wasm is geport, is Kotlin in de vorm van Kotlin/Wasm . De demo , met broncode met dank aan het Kotlin-team, wordt weergegeven in de volgende lijst.

import kotlinx.browser.document
import kotlinx.dom.appendText
import org.w3c.dom.HTMLDivElement

fun main() {
    (document.getElementById("warning") as HTMLDivElement).style.display = "none"
    document.body?.appendText("Hello, ${greet()}!")
}

fun greet() = "world"

Nu vraag je je misschien af ​​wat het punt is, aangezien de bovenstaande Kotlin-code in feite bestaat uit de JavaScript OM API's die zijn geconverteerd naar Kotlin . Het begint logischer te worden in combinatie met Compose Multiplatform , waarmee ontwikkelaars kunnen voortbouwen op de gebruikersinterface die ze mogelijk al hebben gemaakt voor hun Android Kotlin-apps. Bekijk een vroege verkenning hiervan met de Kotlin/Wasm image viewer- demo en verken de broncode , eveneens met dank aan het Kotlin-team.

Dart en Flutter

De Dart- en Flutter-teams van Google bereiden ook ondersteuning voor WasmGC voor. Het compilatiewerk van Dart-to-Wasm is bijna voltooid en het team werkt aan toolingondersteuning voor het leveren van Flutter-webapplicaties die zijn gecompileerd naar WebAssembly. In de Flutter-documentatie kunt u lezen wat de huidige stand van de werkzaamheden is. De volgende demo is de Flutter WasmGC Preview .

Meer informatie over WasmGC

Deze blogpost heeft nauwelijks de oppervlakte betreden en bood vooral een overzicht op hoog niveau van WasmGC. Bekijk deze links voor meer informatie over de functie:

Dankbetuigingen

Dit artikel is beoordeeld door Matthias Liedtke , Adam Klein , Joshua Bell , Alon Zakai , Jakob Kummerow , Clemens Backes , Emanuel Ziegler en Rachel Andrew .