La récupération de mémoire WebAssembly (WasmGC) est désormais activée par défaut dans Chrome

Il existe deux types de langages de programmation : ceux qui utilisent un ramasse-miettes et ceux qui nécessitent une gestion manuelle de la mémoire. Kotlin, PHP ou Java, entre autres, en sont des exemples. C, C++ ou Rust en sont des exemples. En règle générale, les langages de programmation de niveau supérieur sont plus susceptibles d'inclure la récupération de mémoire comme fonctionnalité standard. Cet article de blog se concentre sur ces langages de programmation avec récupération de mémoire et sur la façon dont ils peuvent être compilés en WebAssembly (Wasm). Mais qu'est-ce que le garbage collection (souvent appelé GC) ?

Browser Support

  • Chrome: 119.
  • Edge: 119.
  • Firefox: 120.
  • Safari: 18.2.

Récupération de mémoire

En termes simplifiés, l'idée de la récupération de mémoire est de tenter de récupérer la mémoire allouée par le programme, mais qui n'est plus référencée. Cette mémoire est appelée "mémoire inutilisée". Il existe de nombreuses stratégies pour implémenter la récupération de mémoire. L'une d'elles est le comptage des références, dont l'objectif est de compter le nombre de références aux objets en mémoire. Lorsqu'il n'y a plus de références à un objet, il peut être marqué comme inutilisé et donc prêt pour la récupération de mémoire. Le collecteur de déchets de PHP utilise le comptage des références. La fonction xdebug_debug_zval() de l'extension Xdebug vous permet de jeter un coup d'œil à son fonctionnement. Prenons l'exemple du programme PHP suivant.

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

Le programme attribue un nombre aléatoire converti en chaîne à une nouvelle variable appelée a. Il crée ensuite deux nouvelles variables, b et c, et leur attribue la valeur de a. Il réattribue ensuite b au nombre 42, puis annule la définition de c. Enfin, il définit la valeur de a sur null. En annotant chaque étape du programme avec xdebug_debug_zval(), vous pouvez voir le compteur de référence du garbage collector à l'œuvre.

<?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'exemple ci-dessus génère les journaux suivants, où vous pouvez voir comment le nombre de références à la valeur de la variable a diminue après chaque étape, ce qui est logique compte tenu de la séquence de code. (Votre nombre aléatoire sera bien sûr différent.)

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

Il existe d'autres défis liés au ramasse-miettes, comme la détection des cycles, mais pour cet article, une compréhension de base du comptage des références suffit.

Les langages de programmation sont implémentés dans d'autres langages de programmation.

Cela peut sembler étrange, mais les langages de programmation sont implémentés dans d'autres langages de programmation. Par exemple, le runtime PHP est principalement implémenté en C. Vous pouvez consulter le code source PHP sur GitHub. Le code de récupération de mémoire de PHP se trouve principalement dans le fichier zend_gc.c. La plupart des développeurs installent PHP via le gestionnaire de packages de leur système d'exploitation. Toutefois, les développeurs peuvent également compiler PHP à partir du code source. Par exemple, dans un environnement Linux, les étapes ./buildconf && ./configure && make compileraient PHP pour l'environnement d'exécution Linux. Mais cela signifie également que l'environnement d'exécution PHP peut être compilé pour d'autres environnements d'exécution, comme Wasm.

Méthodes traditionnelles de portage de langages vers l'environnement d'exécution Wasm

Indépendamment de la plate-forme sur laquelle PHP s'exécute, les scripts PHP sont compilés dans le même bytecode et exécutés par le moteur Zend. Zend Engine est un compilateur et un environnement d'exécution pour le langage de script PHP. Il se compose de la machine virtuelle (VM) Zend, qui comprend le compilateur Zend et l'exécuteur Zend. Les langages tels que PHP, qui sont implémentés dans d'autres langages de haut niveau comme C, disposent généralement d'optimisations ciblant des architectures spécifiques, telles qu'Intel ou ARM, et nécessitent un backend différent pour chaque architecture. Dans ce contexte, Wasm représente une nouvelle architecture. Si la VM comporte du code spécifique à l'architecture, comme la compilation juste-à-temps (JIT) ou en amont (AOT), le développeur implémente également un backend pour JIT/AOT pour la nouvelle architecture. Cette approche est très logique, car la partie principale du code peut souvent être recompilée pour chaque nouvelle architecture.

Étant donné le faible niveau de Wasm, il est naturel d'essayer la même approche : recompiler le code de la VM principale avec son analyseur, sa prise en charge de bibliothèque, son collecteur de déchets et son optimiseur en Wasm, et implémenter un backend JIT ou AOT pour Wasm si nécessaire. Cela est possible depuis le MVP Wasm et fonctionne bien dans la pratique dans de nombreux cas. En fait, c'est le PHP compilé en Wasm qui alimente WordPress Playground. Pour en savoir plus sur le projet, consultez l'article Créer des expériences WordPress dans le navigateur avec WordPress Playground et WebAssembly.

Toutefois, PHP Wasm s'exécute dans le navigateur dans le contexte du langage hôte JavaScript. Dans Chrome, JavaScript et Wasm sont exécutés dans V8, le moteur JavaScript Open Source de Google qui implémente ECMAScript tel que spécifié dans ECMA-262. De plus, V8 dispose déjà d'un collecteur de déchets. Cela signifie que les développeurs qui utilisent, par exemple, PHP compilé en Wasm, finissent par expédier une implémentation de collecteur de déchets du langage porté (PHP) au navigateur qui possède déjà un collecteur de déchets, ce qui est aussi gaspilleur que cela en a l'air. C'est là que WasmGC entre en jeu.

L'autre problème de l'ancienne approche, qui consistait à laisser les modules Wasm créer leur propre GC au-dessus de la mémoire linéaire de Wasm, est qu'il n'y a alors aucune interaction entre le propre garbage collector de Wasm et le garbage collector intégré du langage compilé en Wasm, ce qui a tendance à entraîner des problèmes tels que des fuites de mémoire et des tentatives de collecte inefficaces. En permettant aux modules Wasm de réutiliser le GC intégré existant, ces problèmes sont évités.

Portage de langages de programmation vers de nouveaux environnements d'exécution avec WasmGC

WasmGC est une proposition du WebAssembly Community Group. L'implémentation MVP actuelle de Wasm n'est capable de gérer que des nombres (entiers et flottants) dans la mémoire linéaire. Avec la proposition de types de référence, Wasm peut également conserver des références externes. WasmGC ajoute désormais des types de tas structurés et de tableaux, ce qui signifie qu'il est compatible avec l'allocation de mémoire non linéaire. Chaque objet WasmGC possède un type et une structure fixes, ce qui permet aux VM de générer facilement du code efficace pour accéder à leurs champs sans risque de désoptimisation, comme c'est le cas pour les langages dynamiques tels que JavaScript. Cette proposition ajoute donc une prise en charge efficace des langages gérés de haut niveau à WebAssembly, via des types de tas de structures et de tableaux qui permettent aux compilateurs de langage ciblant Wasm de s'intégrer à un récupérateur de mémoire dans la VM hôte. En termes simplifiés, cela signifie qu'avec WasmGC, le portage d'un langage de programmation vers Wasm n'implique plus que le garbage collector du langage de programmation fasse partie du portage. Au lieu de cela, le garbage collector existant peut être utilisé.

Pour vérifier l'impact réel de cette amélioration, l'équipe Wasm de Chrome a compilé des versions du benchmark Fannkuch (qui alloue des structures de données au fur et à mesure de son fonctionnement) à partir de C, Rust et Java. Les binaires C et Rust peuvent varier de 6,1 Ko à 9,6 Ko en fonction des différents indicateurs de compilation, tandis que la version Java est beaucoup plus petite (seulement 2,3 Ko). C et Rust n'incluent pas de collecteur de déchets, mais ils regroupent toujours malloc/free pour gérer la mémoire. La raison pour laquelle Java est plus petit ici est qu'il n'a pas besoin de regrouper de code de gestion de la mémoire. Il ne s'agit que d'un exemple spécifique, mais il montre que les binaires WasmGC peuvent être très petits, et ce avant tout travail important d'optimisation de la taille.

Voir un langage de programmation porté sur WasmGC en action

Kotlin Wasm

Kotlin, sous la forme de Kotlin/Wasm, est l'un des premiers langages de programmation à avoir été porté sur Wasm grâce à WasmGC. La démonstration, avec le code source fourni par l'équipe Kotlin, est présentée dans la liste suivante.

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"

Vous vous demandez peut-être quel est l'intérêt de cette opération, car le code Kotlin ci-dessus se compose essentiellement des API OM JavaScript converties en Kotlin. Cela prend tout son sens lorsqu'il est associé à Compose Multiplatform, qui permet aux développeurs de s'appuyer sur l'UI qu'ils ont peut-être déjà créée pour leurs applications Android Kotlin. Découvrez une première exploration de ce langage avec le lecteur d'images Kotlin/Wasm, également proposé par l'équipe Kotlin.

Démonstration du lecteur d&#39;images Kotlin/Wasm.

Dart et Flutter

Les équipes Dart et Flutter de Google préparent également la prise en charge de WasmGC. La compilation Dart vers Wasm est presque terminée. L'équipe travaille sur la prise en charge des outils pour la diffusion d'applications Web Flutter compilées en WebAssembly. Pour en savoir plus sur l'état actuel des travaux, consultez la documentation Flutter. La démo suivante est la version preview de Flutter WasmGC.

En savoir plus sur WasmGC

Cet article de blog n'a fait qu'effleurer le sujet et a surtout fourni une vue d'ensemble de WasmGC. Pour en savoir plus sur cette fonctionnalité, consultez les liens suivants :

Remerciements

Cet article a été relu par Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler et Rachel Andrew.