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: les langages de programmation avec garbage collection et les langages de programmation qui nécessitent une gestion manuelle de la mémoire. Kotlin, PHP ou Java sont des exemples de langages de programmation de ce type. C, C++ ou Rust en sont des exemples. En règle générale, les langages de programmation de haut niveau sont plus susceptibles de proposer la récupération de mémoire comme fonctionnalité standard. Dans cet article de blog, nous nous concentrons 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) ?

Navigateurs pris en charge

  • 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 consiste à 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 "garbage". Il existe de nombreuses stratégies d'implémentation du garbage collection. 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 à être collecté. Le garbage collector de PHP utilise le comptage des références. La fonction xdebug_debug_zval() de l'extension Xdebug vous permet d'en savoir plus sur son fonctionnement. Prenons le 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 a. Ensuite, il réattribue b au numéro 42, puis supprime 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érences 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, qui montrent 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. (Bien entendu, votre nombre aléatoire sera 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

Le garbage collection présente d'autres défis, comme la détection de cycles, mais pour cet article, un niveau de compréhension de base du comptage des références est suffisant.

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

Vous pouvez avoir l'impression que c'est le début, mais les langages de programmation sont implémentés dans d'autres langages de programmation. Par exemple, l'environnement d'exécution PHP est principalement implémenté en C. Vous pouvez consulter le code source PHP sur GitHub. Le code de récupération des déchets de PHP se trouve principalement dans le fichier zend_gc.c. La plupart des développeurs installent PHP via le gestionnaire de paquets de leur système d'exploitation. Les développeurs peuvent également compiler PHP à partir du code source. Par exemple, dans un environnement Linux, les étapes ./buildconf && ./configure && make compilent PHP pour l'environnement d'exécution Linux. 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 comme PHP, implémentés dans d'autres langages de haut niveau comme C, comportent généralement des optimisations qui ciblent 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 contient du code spécifique à l'architecture, comme la compilation JIT (juste-à-temps) ou AOT (en amont), 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 codebase peut souvent être simplement recompilée pour chaque nouvelle architecture.

Étant donné que Wasm est de bas niveau, il est naturel d'essayer la même approche: recompiler le code de la VM principale avec son analyseur, sa prise en charge de la bibliothèque, son garbage collection 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 le WordPress Playground. Pour en savoir plus sur ce 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 comme spécifié dans la norme ECMA-262. De plus, V8 dispose déjà d'un ramasse-ordures. Cela signifie que les développeurs qui utilisent, par exemple, du code PHP compilé en Wasm, finissent par envoyer une implémentation du collecteur de déchets du langage porté (PHP) au navigateur qui dispose déjà d'un collecteur de déchets, ce qui est aussi inutile que cela puisse paraître. C'est là que WasmGC intervient.

L'autre problème de l'ancienne approche consistant à 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é au langage compilé en Wasm, ce qui tend à causer des problèmes tels que des fuites de mémoire et des tentatives de collecte inefficaces. Permettre aux modules Wasm de réutiliser le GC intégré existant évite ces problèmes.

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

WasmGC est une proposition du groupe de la communauté WebAssembly. L'implémentation actuelle du MVP Wasm ne peut gérer que des nombres, c'est-à-dire des entiers et des nombres à virgule flottante, dans une mémoire linéaire. Avec la proposition de types de référence en cours de déploiement, Wasm peut également conserver des références externes. WasmGC ajoute désormais des types de tas de struct et de tableau, ce qui permet de prendre en charge l'allocation de mémoire non linéaire. Chaque objet WasmGC a 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 deoptimizations que présentent les langages dynamiques tels que JavaScript. Cette proposition ajoute ainsi une prise en charge efficace des langages gérés de haut niveau à WebAssembly, via des types de tas de struct et de tableaux qui permettent aux compilateurs de langages ciblant Wasm de s'intégrer à un garbage collector dans la VM hôte. En termes simplifiés, cela signifie qu'avec WasmGC, le portage d'un langage de programmation vers Wasm signifie que le garbage collector du langage de programmation n'a plus besoin de faire partie du port, mais que le garbage collector existant peut être utilisé à la place.

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 aller de 6,1 ko à 9,6 ko, selon les différents indicateurs de compilation, tandis que la version Java est beaucoup plus petite, avec seulement 2,3 ko. C et Rust n'incluent pas de garbage collector, mais ils regroupent toujours malloc/free pour gérer la mémoire. Java est plus petit ici, car il n'a pas besoin de regrouper du tout de code de gestion de la mémoire. Il ne s'agit là que d'un exemple spécifique, mais il montre que les binaires WasmGC peuvent être très petits, et ce même avant tout travail d'optimisation de la taille.

Voir un langage de programmation porté par WasmGC en action

Kotlin Wasm

L'un des premiers langages de programmation à avoir été porté vers Wasm grâce à WasmGC est Kotlin sous la forme de Kotlin/Wasm. La démonstration, avec le code source mis à disposition 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 à quoi cela sert, car le code Kotlin ci-dessus consiste essentiellement en des API OM JavaScript converties en Kotlin. Il commence à avoir plus de sens en combinaison avec la multiplateforme Compose, 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 cette fonctionnalité avec la démonstration du visionneuse d'images Kotlin/Wasm et son code source, également mis à disposition par l'équipe Kotlin.

Dart et Flutter

Les équipes Dart et Flutter de Google préparent également la prise en charge de WasmGC. Le travail de compilation Dart vers Wasm est presque terminé, et 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 de ce projet, consultez la documentation Flutter. La démonstration suivante est la version preview de Flutter WasmGC.

En savoir plus sur WasmGC

Cet article de blog n'a fait qu'effleurer la surface 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.