Esistono due tipi di linguaggi di programmazione: quelli con garbage collection e quelli che richiedono la gestione manuale della memoria. Esempi di questi linguaggi sono Kotlin, PHP o Java. Esempi di questi ultimi sono C, C++ o Rust. Come regola generale, i linguaggi di programmazione di livello superiore hanno maggiori probabilità di avere la garbage collection come funzionalità standard. Questo post del blog si concentra su questi linguaggi di programmazione con garbage collection e su come possono essere compilati in WebAssembly (Wasm). Ma cos'è innanzitutto la garbage collection (spesso indicata come GC)?
Browser Support
Garbage collection
In termini semplificati, l'idea della garbage collection è il tentativo di recuperare la memoria allocata dal programma, ma a cui non viene più fatto riferimento. Questa memoria è chiamata spazzatura. Esistono molte strategie per implementare la garbage collection. Uno di questi è il conteggio dei riferimenti, in cui l'obiettivo è contare il numero di riferimenti agli oggetti in memoria. Quando non ci sono più riferimenti a un oggetto, questo può essere contrassegnato come non più utilizzato e quindi pronto per la garbage collection. Il garbage collector di PHP utilizza il conteggio dei riferimenti e l'utilizzo della funzione xdebug_debug_zval()
dell'estensione Xdebug ti consente di dare un'occhiata al suo funzionamento interno. Considera il seguente programma PHP.
<?php
$a= (string) rand();
$c = $b = $a;
$b = 42;
unset($c);
$a = null;
?>
Il programma assegna un numero casuale convertito in una stringa a una nuova variabile denominata a
. Quindi crea due nuove variabili, b
e c
, e assegna loro il valore a
. Dopodiché, riassegna b
al numero 42
e annulla l'impostazione di c
. Infine, imposta il valore di a
su null
. Annotando ogni passaggio del programma con xdebug_debug_zval()
, puoi vedere il contatore dei riferimenti del garbage collector in azione.
<?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');
?>
L'esempio precedente genererà i seguenti log, in cui puoi vedere come il numero di riferimenti al valore della variabile a
diminuisce dopo ogni passaggio, il che è logico data la sequenza di codice. Il tuo numero casuale sarà diverso, ovviamente.
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
Esistono altre sfide con la garbage collection, come il rilevamento dei cicli, ma per questo articolo è sufficiente una comprensione di base del conteggio dei riferimenti.
I linguaggi di programmazione vengono implementati in altri linguaggi di programmazione
Potrebbe sembrare un'illusione, ma i linguaggi di programmazione vengono implementati in altri linguaggi di programmazione. Ad esempio, il runtime PHP è implementato principalmente in C. Puoi consultare il codice sorgente PHP su GitHub. Il codice di garbage collection di PHP si trova principalmente nel file zend_gc.c
. La maggior parte degli sviluppatori installerà PHP tramite il gestore di pacchetti del proprio sistema operativo. Tuttavia, gli sviluppatori possono anche creare PHP dal codice sorgente. Ad esempio, in un ambiente Linux, i passaggi ./buildconf && ./configure && make
compilerebbero PHP per il runtime Linux. Ma questo significa anche che il runtime PHP può essere compilato per altri runtime, come, indovina un po', Wasm.
Metodi tradizionali di porting dei linguaggi nel runtime Wasm
Indipendentemente dalla piattaforma su cui viene eseguito PHP, gli script PHP vengono compilati nello stesso bytecode ed eseguiti da Zend Engine. Zend Engine è un compilatore e un ambiente di runtime per il linguaggio di scripting PHP. È costituita dalla Zend Virtual Machine (VM), composta da Zend Compiler e Zend Executor. Linguaggi come PHP, implementati in altri linguaggi di alto livello come C, in genere hanno ottimizzazioni che hanno come target architetture specifiche, come Intel o ARM, e richiedono un backend diverso per ogni architettura. In questo contesto, Wasm rappresenta una nuova architettura. Se la VM ha codice specifico per l'architettura, come la compilazione just-in-time (JIT) o ahead-of-time (AOT), lo sviluppatore implementa anche un backend per JIT/AOT per la nuova architettura. Questo approccio ha molto senso perché spesso la parte principale del codebase può essere ricompilata per ogni nuova architettura.
Data la natura di basso livello di Wasm, è naturale provare lo stesso approccio: ricompilare il codice della VM principale con il relativo parser, il supporto della libreria, la garbage collection e l'ottimizzatore in Wasm e implementare un backend JIT o AOT per Wasm, se necessario. Ciò è possibile fin dalla MVP di Wasm e funziona bene nella pratica in molti casi. Infatti, PHP compilato in Wasm è ciò che alimenta WordPress Playground. Scopri di più sul progetto nell'articolo Crea esperienze WordPress nel browser con WordPress Playground e WebAssembly.
Tuttavia, PHP Wasm viene eseguito nel browser nel contesto del linguaggio host JavaScript. In Chrome, JavaScript e Wasm vengono eseguiti in V8, il motore JavaScript open source di Google che implementa ECMAScript come specificato in ECMA-262. Inoltre, V8 ha già un garbage collector. Ciò significa che gli sviluppatori che utilizzano, ad esempio, PHP compilato in Wasm, finiscono per distribuire un'implementazione del garbage collector del linguaggio di cui è stato eseguito il porting (PHP) al browser che ha già un garbage collector, il che è uno spreco. È qui che entra in gioco WasmGC.
L'altro problema del vecchio approccio che consentiva ai moduli Wasm di creare la propria GC sopra la memoria lineare di Wasm è che non c'è interazione tra il garbage collector di Wasm e quello integrato del linguaggio compilato in Wasm, il che tende a causare problemi come perdite di memoria e tentativi di raccolta inefficienti. Consentire ai moduli Wasm di riutilizzare la GC integrata esistente evita questi problemi.
Portare linguaggi di programmazione su nuovi runtime con WasmGC
WasmGC è una proposta del WebAssembly Community Group. L'attuale implementazione di Wasm MVP è in grado di gestire solo numeri, ovvero numeri interi e numeri in virgola mobile, nella memoria lineare e, con la proposta di tipi di riferimento in fase di implementazione, Wasm può anche conservare riferimenti esterni. WasmGC ora aggiunge tipi di heap di struct e array, il che significa supporto per l'allocazione di memoria non lineare. Ogni oggetto WasmGC ha un tipo e una struttura fissi, il che consente alle VM di generare facilmente codice efficiente per accedere ai propri campi senza il rischio di deottimizzazioni che hanno linguaggi dinamici come JavaScript. Questa proposta aggiunge quindi un supporto efficiente per i linguaggi gestiti di alto livello a WebAssembly, tramite tipi di heap di struct e array che consentono ai compilatori di linguaggi che hanno come target Wasm di integrarsi con un garbage collector nella VM host. In termini semplificati, ciò significa che con WasmGC, il porting di un linguaggio di programmazione a Wasm non richiede più che il garbage collector del linguaggio di programmazione faccia parte del porting, ma può essere utilizzato il garbage collector esistente.
Per verificare l'impatto reale di questo miglioramento, il team Wasm di Chrome ha compilato versioni del benchmark Fannkuch (che alloca le strutture di dati durante il funzionamento) da C, Rust e Java. I binari C e Rust possono variare da 6,1 K a 9,6 K a seconda dei vari flag del compilatore, mentre la versione Java è molto più piccola, solo 2,3 K. C e Rust non includono un garbage collector, ma raggruppano comunque malloc/free
per gestire la memoria. Il motivo per cui Java è più piccolo è che non ha bisogno di raggruppare alcun codice di gestione della memoria. Questo è solo un esempio specifico, ma dimostra che i binari WasmGC hanno il potenziale di essere molto piccoli, e questo prima ancora di qualsiasi lavoro significativo di ottimizzazione per le dimensioni.
Vedere un linguaggio di programmazione di cui è stato eseguito il porting di WasmGC in azione
Kotlin Wasm
Uno dei primi linguaggi di programmazione di cui è stato eseguito il porting su Wasm grazie a WasmGC è Kotlin sotto forma di Kotlin/Wasm. La demo, con il codice sorgente per gentile concessione del team Kotlin, è mostrata nel seguente elenco.
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"
A questo punto ti starai chiedendo qual è lo scopo, dato che il codice Kotlin riportato sopra è costituito essenzialmente dalle API JavaScript OM convertite in Kotlin. Inizia ad avere più senso in combinazione con Compose Multiplatform, che consente agli sviluppatori di basarsi sull'interfaccia utente che potrebbero aver già creato per le loro app Android Kotlin. Dai un'occhiata a un'esplorazione preliminare di questo argomento con il visualizzatore di immagini Kotlin/Wasm, sempre per gentile concessione del team Kotlin.
Dart e Flutter
Anche i team Dart e Flutter di Google stanno preparando il supporto per WasmGC. Il lavoro di compilazione da Dart a Wasm è quasi completato e il team sta lavorando al supporto degli strumenti per la distribuzione di applicazioni web Flutter compilate in WebAssembly. Puoi leggere lo stato attuale del lavoro nella documentazione di Flutter. La seguente demo è l'anteprima di Flutter WasmGC.
Scopri di più su WasmGC
Questo post del blog ha appena scalfito la superficie e ha fornito principalmente una panoramica generale di WasmGC. Per saperne di più sulla funzionalità, consulta questi link:
- Un nuovo modo per portare in modo efficiente su WebAssembly i linguaggi di programmazione con garbage collection
- Panoramica di WasmGC
- WasmGC MVP
- WasmGC post-MVP
Ringraziamenti
Questo articolo è stato esaminato da Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler e Rachel Andrew.