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. Par exemple, Kotlin, PHP ou Java. C, C++ ou Rust, par exemple, En règle générale, les langages de programmation de niveau supérieur sont plus susceptibles d'avoir la récupération de mémoire comme fonctionnalité standard. Dans cet article de blog, l'accent est mis sur ces langages de programmation récupérés de mémoire et sur la manière dont ils peuvent être compilés dans WebAssembly (Wasm). Mais en quoi consiste la récupération de mémoire (souvent appelée "Récupération de mémoire") ?

Navigateurs pris en charge

  • 119
  • 119
  • 120
  • x

Récupération de mémoire

Pour faire simple, 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 "mauvaise mémoire". Il existe de nombreuses stratégies pour implémenter la récupération de mémoire. L'une d'elles est la comptabilisation de références, l'objectif étant de compter le nombre de références à des 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 d'en savoir plus. 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 a. Ensuite, il réattribue b au numéro 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 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. Vous pouvez voir que 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 défis, comme la détection des cycles, mais pour cet article, il suffit de comprendre les bases du comptage des références.

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

Même si cela peut sembler évident, certains 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 langage 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 créer PHP à partir du code source. Par exemple, dans un environnement Linux, les étapes ./buildconf && ./configure && make permettent de compiler 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, vous l'aurez deviné.

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

Indépendamment de la plate-forme sur laquelle PHP est exécuté, les scripts PHP sont compilés dans le même bytecode et sont exécutés par Zend Engine. 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 Zend et de l'exécuteur Zend. Les langages comme PHP qui sont implémentés dans d'autres langages de haut niveau comme le C ont 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 dispose d'un code spécifique à l'architecture, comme une compilation juste-à-temps (JIT) ou anticipée (AOT), le développeur implémente également un backend pour le JIT/AOT pour la nouvelle architecture. Cette approche est très judicieuse, 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 prise en charge de la bibliothèque, la récupération de mémoire et son optimiseur sur Wasm, et implémentez un backend JIT ou AOT pour Wasm si nécessaire. Cela est possible depuis le MVP Wasm, et cela fonctionne bien en pratique dans de nombreux cas. D'ailleurs, WordPress Playground repose sur PHP compilé vers Wasm. Pour en savoir plus sur le projet, consultez l'article Créer des expériences WordPress intégrées au navigateur avec WordPress Playground et WebAssembly.

Cependant, PHP Wasm s'exécute dans le navigateur dans le contexte du JavaScript du langage hôte. Dans Chrome, JavaScript et Wasm s'exécutent dans V8, le moteur JavaScript Open Source de Google qui implémente ECMAScript, comme spécifié dans ECMA-262. V8 dispose déjà d'un récupérateur de mémoire. Cela signifie que les développeurs qui utilisent, par exemple, le PHP compilé pour Wasm, finissent par envoyer une mise en œuvre du récupérateur de mémoire du langage porté (PHP) vers le navigateur qui dispose déjà d'un récupérateur de mémoire, ce qui est aussi inefficace qu'il n'y paraît. C'est là que WasmGC entre en jeu.

L'autre problème de l'ancienne approche, qui consiste à laisser les modules Wasm créer leur propre récupération de mémoire sur 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, qui a tendance à causer des problèmes tels que des fuites de mémoire et des tentatives de récupération inefficaces. Vous pouvez éviter ces problèmes en laissant les modules Wasm réutiliser la récupération de mémoire intégrée existante.

Portage 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 permet de traiter que des nombres, c'est-à-dire des nombres entiers et des nombres à virgule flottante, en mémoire linéaire. Et avec la proposition de types de référence envoyée, Wasm peut également conserver des références externes. WasmGC ajoute désormais des types de tas de mémoire pour les structures et les tableaux, ce qui vous permet de prendre en charge l'allocation de mémoire non linéaire. Chaque objet WasmGC présente 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ésoptimiser les 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 de structure et de tableau, qui permettent aux compilateurs de langage ciblant Wasm d'intégrer un récupérateur de mémoire dans la VM hôte. Pour simplifier, 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é.

Pour vérifier l'impact réel de cette amélioration, l'équipe Wasm de Chrome a compilé des versions du benchmark Fannkuch (qui attribue les structures de données en temps réel) à partir de C, Rust et Java. Les binaires C et Rust peuvent se situer entre 6.1 K et 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 incluent tout de même malloc/free pour gérer la mémoire. Java est plus petit ici, car il n'a pas besoin d'intégrer de code de gestion de mémoire. Ce n'est qu'un exemple spécifique, mais il montre que les binaires WasmGC ont le potentiel d'être très petits, et cela est même avant tout travail important d'optimisation de la taille.

Découvrir un langage de programmation porté par WasmGC en action

Wasm Kotlin

L'un des premiers langages de programmation à avoir été portés sur Wasm grâce à WasmGC est Kotlin sous la forme de Kotlin/Wasm. La démonstration, avec le code source fournie 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, puisque le code Kotlin ci-dessus est constitué des API JavaScript OM converties en Kotlin. Elle commence à être plus logique lorsqu'elle est associée à 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 en Kotlin. Découvrez-le en avant-première avec la démo de la visionneuse d'images Kotlin/Wasm et explorez son code source, également gracieux de 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-to-Wasm est presque terminé, et l'équipe travaille actuellement à la compatibilité avec les outils permettant de 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 la preview de WasmGC pour Flutter.

En savoir plus sur WasmGC

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

Remerciements

Image principale 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.