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 récupération de mémoire et les langages de programmation qui nécessitent une gestion manuelle de la mémoire. Parmi les premiers exemples, citons Kotlin, PHP ou Java. En voici des exemples : C, C++ ou Rust. En règle générale, les langages de programmation de niveau supérieur ont plus de chances d'avoir la récupération de mémoire comme fonctionnalité standard. Cet article de blog met l'accent sur ces langages de programmation récupérés en mémoire et sur leur compilation dans WebAssembly (Wasm). Mais qu'est-ce que la récupération de mémoire (souvent appelée Récupération de mémoire) pour commencer ?

Navigateurs pris en charge

  • Chrome: 119 <ph type="x-smartling-placeholder">
  • Edge: 119 <ph type="x-smartling-placeholder">
  • Firefox: 120 <ph type="x-smartling-placeholder">
  • Safari: non compatible. <ph type="x-smartling-placeholder">

Récupération de mémoire

Pour faire simple, l'idée de récupération de mémoire est la tentative de récupération de la mémoire allouée par le programme, mais qui n'est plus référencée. On parle de mémoire de ce type. Il existe de nombreuses stratégies pour implémenter la récupération de mémoire. L'une de ces méthodes est le comptabilisation 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, celui-ci peut être marqué comme n'étant plus utilisé et donc prêt pour la récupération de mémoire. Le récupérateur de mémoire de PHP utilise le comptage des références, et l'utilisation de la fonction xdebug_debug_zval() de l'extension Xdebug vous permet de jeter un œil en arrière-plan. 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 variables, b et c, et leur attribue la valeur de a. Ensuite, il réattribue 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 observer le compteur de référence du récupérateur de mémoire en action.

<?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, dans lesquels 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. (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

La récupération de mémoire présente d'autres difficultés, telles que la détection des cycles, mais pour cet article, un niveau de compréhension de base de la comptabilisation des références suffit.

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

Cela peut ressembler à du lancement, 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 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. Les développeurs peuvent également créer PHP à partir du code source. Par exemple, dans un environnement Linux, la procédure ./buildconf && ./configure && make permet de compiler du 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, comme vous l'avez deviné.

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

Indépendamment de la plate-forme sur laquelle s'exécute PHP, 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. Elle se compose de la machine virtuelle (VM) Zend, composée du compilateur et de l'exécuteur Zend. Les langages tels que PHP qui sont implémentés dans d'autres langages de haut niveau comme le C présentent généralement des optimisations qui ciblent des architectures spécifiques, comme 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 propre à l'architecture, comme une compilation juste-à-temps (JIT) ou une compilation à l'avance (AOT), le développeur implémente également un backend 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é le faible niveau de Wasm, il est naturel d'essayer la même approche: recompilez le code de la VM principale avec son analyseur, sa compatibilité avec la bibliothèque, sa récupération de mémoire et son optimiseur, et implémentez un backend JIT ou AOT pour Wasm si nécessaire. Cela est possible depuis le MVP de Wasm et fonctionne bien dans la pratique dans de nombreux cas. D'ailleurs, WordPress Playground repose sur PHP compilé dans Wasm. 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 JavaScript dans le langage hôte. Dans Chrome, JavaScript et Wasm sont exécutés en V8, le moteur JavaScript Open Source de Google qui implémente ECMAScript comme spécifié dans ECMA-262. De plus, V8 dispose déjà d'un récupérateur de mémoire. Cela signifie que les développeurs qui utilisent, par exemple, le langage PHP compilé dans Wasm, finissent par envoyer une implémentation de récupérateur de mémoire du langage portable (PHP) au navigateur qui dispose déjà d'un récupérateur de mémoire, ce qui est tout aussi inutile. C'est là qu'intervient WasmGC.

L'autre problème de l'ancienne approche consistant à laisser les modules Wasm créer leur propre récupération de mémoire en plus de la mémoire linéaire de Wasm est qu'il n'y a alors aucune interaction entre le récupérateur de mémoire de Wasm et le récupérateur de mémoire intégré du langage compilé vers Wasm, ce qui a tendance à causer des problèmes tels que des fuites de mémoire et des tentatives de récupération inefficaces. Pour éviter ces problèmes, autorisez les modules Wasm à réutiliser la récupération de mémoire intégrée existante.

Porter des 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 de Wasm MVP ne peut traiter que les nombres, c'est-à-dire les entiers et les flottants, en mémoire linéaire. De plus, avec la proposition de types de référence en cours d'envoi, Wasm peut également conserver des références externes. WasmGC ajoute désormais des types de segments de mémoire struct et Array, 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 risquer de désoptimisation des langages dynamiques tels que JavaScript. Cette proposition ajoute ainsi une prise en charge efficace des langages gérés de haut niveau dans WebAssembly, via des types de segments de mémoire struct et Array 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 d'autres termes, cela signifie qu'avec WasmGC, le portage d'un langage de programmation vers Wasm signifie que le récupérateur de mémoire du langage de programmation n'a plus besoin de faire partie du port, mais que le récupérateur de mémoire existant peut être utilisé à la place.

Pour vérifier l'impact concret de cette amélioration, l'équipe Wasm de Chrome a compilé des versions du benchmark Fannkuch (qui alloue les structures de données en fonction de son fonctionnement) à partir de C, Rust et Java. La taille des binaires C et Rust peut varier de 6,1 K à 9,6 K selon les différents indicateurs de compilation, tandis que la version Java est beaucoup plus petite (seulement 2,3 K). C et Rust n'incluent pas de récupérateur de mémoire, mais ils regroupent tout de même malloc/free pour gérer la mémoire. Si Java est plus petit, c'est parce qu'il n'a pas besoin de grouper de code de gestion de 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, même avant tout travail important d'optimisation de la taille.

Voir un langage de programmation porté WasmGC en action

Kotlin Wasm

Kotlin, sous la forme Kotlin/Wasm, est l'un des premiers langages de programmation à avoir été portés vers Wasm grâce à WasmGC. La démonstration, avec le code source fourni par l'équipe Kotlin, est présentée dans la fiche 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, puisque le code Kotlin ci-dessus se compose essentiellement des API OM JavaScript converties en Kotlin. L'utilisation de la multiplateforme Compose commence à s'avérer plus judicieuse, car elle permet aux développeurs de s'appuyer sur l'interface utilisateur qu'ils ont peut-être déjà créée pour leurs applications Android en Kotlin. Découvrez une exploration en avant-première avec la démo du lecteur d'images Kotlin/Wasm et explorez son code source, également fourni par l'équipe Kotlin.

Dart et Flutter

Les équipes Dart et Flutter chez Google préparent également la compatibilité avec WasmGC. Le travail de compilation Dart-to-Wasm est presque terminé, et l'équipe travaille sur la compatibilité des outils pour fournir des applications Web Flutter compilées dans WebAssembly. Pour en savoir plus sur l'état actuel du travail, consultez la documentation Flutter. La démonstration suivante est une aperçu de WasmGC pour Flutter.

En savoir plus sur WasmGC

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

Remerciements

Image héros de Gary Chan sur Unsplash. Cet article a été lu par Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler et Rachel Andrew.