- Lors du portage vers WebAssembly de langages à GC comme Java, Kotlin, Dart, Python ou C#, WasmGC n’implique pas de recompiler une VM existante : il exploite les structures, tableaux et le système de types GC propres à Wasm
- Le portage traditionnel vers WasmMVP permet de réutiliser facilement la VM existante et ses optimisations, mais il a le coût d’embarquer un GC ou
malloc/freedans la mémoire linéaire, ainsi que des contraintes liées aux références sur la pile, aux références cycliques et à la fragmentation - Avec WasmGC, la VM gère directement les objets et la mémoire, ce qui peut réduire la taille des binaires ; dans le benchmark
fannkuch, WasmGC faisait 2,3 K, contre 6,1 à 9,6 K pour C/Rust - WasmGC étant une représentation intermédiaire de plus haut niveau, Binaryen
wasm-optet les optimisations d’exécution de V8 sont importants ; on observe par exemple un gain moyen de 1,9× sur des benchmarks Java et d’environ 30 % sur Google Sheets Calc Engine - La standardisation et la prise en charge par les navigateurs ont avancé, mais comme il ne s’agit pas de compiler telle quelle une VM existante, il faut un nouveau travail de toolchain pour abaisser les structures du langage vers les primitives WasmGC
Deux façons de porter des langages à GC vers Wasm
- Le portage de langages à GC vers WebAssembly se divise globalement en deux approches
- Portage traditionnel : compiler la VM existante du langage vers WasmMVP, le Minimum Viable Product de WebAssembly lancé en 2017
- Portage WasmGC : compiler le langage vers les structures GC de Wasm définies dans la proposition GC
- Le portage traditionnel ressemble au modèle utilisé lorsqu’on porte un langage vers une nouvelle architecture CPU : on partage le parseur, le support des bibliothèques, le GC et l’optimiseur de la VM, et l’on ajoute seulement un nouveau backend
- Le portage WasmGC ressemble plutôt à un portage du langage vers une nouvelle VM, et non vers une nouvelle architecture
- Comme J2CL, qui compile Java vers JavaScript, les objets du langage sont représentés comme des objets de la VM cible et gérés par le GC de cette VM
- WasmGC vise un niveau plus bas que les VM JavaScript, la JVM ou le CLR, mais il est de plus haut niveau que WasmMVP en ce qu’il fournit des structures et tableaux gérés par la VM, ainsi que des relations de types
Avantages et limites du portage traditionnel vers WasmMVP
- Le principal avantage du portage traditionnel est de pouvoir réutiliser presque tel quel le code de la VM existante, l’implémentation du langage et ses optimisations
- Les sorties WasmMVP utilisent les structures de base de WasmMVP, comme la mémoire linéaire, les tables et les fonctions
- Wasm s’exécute souvent dans des VM qui disposent déjà d’un GC, comme les navigateurs, Node.js, workerd, Deno ou Bun
- Dans cet environnement, inclure une implémentation de GC dans le binaire Wasm entraîne une augmentation de taille inutile
- Même les langages utilisant la mémoire linéaire, comme C, C++ et Rust, doivent embarquer le code
malloc/freelorsqu’ils font des allocations significatives dlmallocfait 6 K, etemmalloc, optimisé pour la taille, dépasse aussi 1 K
- WasmGC gérant automatiquement la mémoire via la VM, il n’est pas nécessaire d’intégrer un GC ou
malloc/freedans Wasm- Dans le benchmark
fannkuchde l’article précédent sur WasmGC, WasmGC faisait 2,3 K, contre 6,1 à 9,6 K pour C/Rust
- Dans le benchmark
Collecte des cycles, références sur la pile et fragmentation
- Dans le navigateur, Wasm interagit souvent avec JavaScript et les Web API, mais avec WasmMVP et les reference types seuls, la collecte fine des cycles est difficile pour les liens bidirectionnels entre Wasm et JS
- Les liens vers des objets JS ne peuvent être placés que dans des tables Wasm
- Les liens de JS vers Wasm peuvent référencer toute l’instance Wasm comme un gros objet unique
- WasmGC définit des objets Wasm reconnus par la VM, ce qui permet de créer des références appropriées entre Wasm et JavaScript
- Les langages à GC doivent aussi reconnaître les références sur la pile, comme les variables locales dans la portée d’un appel
- Dans un portage traditionnel, le sandboxing de Wasm empêche un programme d’inspecter sa propre pile
- Une alternative consiste à utiliser une shadow stack, ou à n’exécuter le GC que lorsqu’il n’y a pas de références sur la pile, par exemple entre deux tours de l’event loop JavaScript
- Une future prise en charge du stack scanning dans Wasm pourrait aider les portages traditionnels
- Aujourd’hui, WasmGC est la méthode qui gère automatiquement les références sur la pile sans surcharge
malloc/freedans la mémoire linéaire WasmMVP peut créer de la fragmentation mémoire dans les programmes de longue durée- Même si la mémoire inutilisée totale est suffisante, une grosse allocation peut échouer s’il n’existe pas de grand bloc contigu
- La fragmentation peut obliger un module Wasm à agrandir sa mémoire plus souvent, entraînant de la surcharge et des erreurs out-of-memory
- WasmGC permet à la VM de déplacer les objets pour compacter le tas GC, ce qui évite la fragmentation
Outils développeur et sémantique des langages
- Dans un portage traditionnel vers WasmMVP, les objets sont placés sous forme d’octets dans la mémoire linéaire, ce qui rend difficile pour les outils développeur d’afficher des informations de types de haut niveau
- Avec WasmGC, la VM gère les objets GC, ce qui facilite l’intégration des outils
- L’onglet Memory de Chrome DevTools permet de voir un heap snapshot d’un programme WasmGC
- Dans l’exemple de liste chaînée, le nom de type
$Nodeet le champ$nextqui référence l’objet suivant sont affichés - Les informations habituelles de heap snapshot, comme le nombre d’objets, la shallow size et la retained size, sont également fournies
- Le debugger de Chrome DevTools fonctionne aussi avec les objets WasmGC
- Le portage traditionnel recompilant la VM existante, il est facile d’obtenir la sémantique attendue du langage telle quelle
- Le portage WasmGC devant représenter les structures du langage avec de nouveaux types GC, structures et tableaux, des compromis sémantiques peuvent être nécessaires pour l’efficacité
- Les champs de structures WasmGC ayant des index et des types fixes, les langages qui souhaitent accéder aux champs de façon plus dynamique peuvent rencontrer des difficultés
- WasmGC ne dispose pas actuellement d’interior pointer, et l’on peut s’attendre à ce que ces limites s’améliorent avec le temps
- Dans d’autres cas de compilation vers une VM cible, des choix sémantiques existent aussi
- Le comportement des nombres de dart2js diffère de celui de la VM Dart
- Les chaînes d’IronPython se comportent comme des chaînes C#
- En revanche, dart2wasm fonctionne sans compromis sur la sémantique numérique, car Wasm peut représenter efficacement les types numériques nécessaires à Dart
Travail de toolchain nécessaire pour un portage WasmGC
- Un portage WasmGC ne consiste pas simplement à recompiler une VM existante
- Une partie du code qui ne s’intègre pas directement au GC d’exécution, comme la logique de parsing ou les optimisations AOT, peut être réutilisée
- En général, beaucoup de nouveau code est nécessaire pour abaisser les structures du langage vers les structures et tableaux WasmGC
- Une VM écrite en C, comme la VM Lua, peut être compilée vers Wasm en quelques minutes, mais un portage WasmGC de Lua doit décider comment représenter les structures de Lua dans les contraintes du système de types WasmGC, puis l’implémenter
- Le principal inconvénient du portage WasmGC est l’effort de toolchain
- L’état idéal serait que le système de types WasmGC prenne efficacement en charge tous les langages, et que chaque langage implémente son port WasmGC
- De futures fonctionnalités du système de types WasmGC peuvent aider sur la première partie
- Partager le travail de toolchain commun peut réduire la charge de la seconde partie
Pourquoi WasmGC peut être mieux optimisé
- WasmGC est une représentation intermédiaire de plus haut niveau que WasmMVP, ce qui laisse davantage de marge d’optimisation
- Dans une fonction d’exemple qui alloue deux fois un objet GC, stocke
10dans un champ puis le renvoie, le code peut logiquement être réduit àreturn 10- Avec WasmMVP, l’allocation devient un appel à
malloc, et commemallocest une fonction complexe qui a des effets de bord sur la mémoire linéaire, il est difficile pour l’optimiseur d’être certain que la deuxième allocation ne modifie pas le champ du premier objet - Avec WasmGC, l’allocation est représentée par l’instruction
struct.newet peut être inférée comme une opération de VM, ce qui permet de suivre les références et les valeurs de champs
- Avec WasmMVP, l’allocation devient un appel à
- WasmGC fournit aussi des pointeurs de fonction explicites
ref.func, les appels via ceux-ci aveccall_ref, ainsi que des types de champs pour structures et tableaux - Si WasmMVP peut atteindre des performances proches du natif, c’est généralement parce que de puissants compilateurs d’optimisation comme LLVM effectuent la plupart des optimisations avant la génération de Wasm
- LLVM ne prend pas en charge WasmGC, et beaucoup de langages à GC n’utilisent pas non plus LLVM ; WasmGC a donc besoin d’un autre modèle d’optimisation
Optimisations Binaryen et wasm-opt
- Avec WasmGC, les optimisations généralistes peuvent être effectuées après l’abaissement vers Wasm, permettant à plusieurs toolchains de langages de partager un optimiseur commun Wasm-to-Wasm
- L’équipe V8 a investi dans la prise en charge de WasmGC par Binaryen, un projet d’optimisation de toolchain WebAssembly
- Chaque toolchain peut utiliser Binaryen via l’outil en ligne de commande
wasm-opt
- Chaque toolchain peut utiliser Binaryen via l’outil en ligne de commande
- Binaryen dispose déjà d’optimisations pour WasmMVP, comme l’inlining, la propagation de constantes et l’élimination de code mort, et la plupart s’appliquent aussi à WasmGC
- Les principales optimisations ajoutées pour WasmGC sont les suivantes
- Escape analysis : déplacer les allocations du tas vers des variables locales
- Devirtualization : convertir des appels indirects en appels directs
- Élimination globale du code mort plus puissante
- Whole-program type-aware content flow analysis, GUFA
- Cast optimizations : suppression des casts redondants et déplacement des positions de cast
- Type pruning
- Type merging
- Raffinement des types pour les variables locales, globales, les champs et les signatures
- Les nouvelles optimisations GC de Binaryen et leur utilisation sont documentées dans les docs Binaryen
- Lors de mesures de performances Java sur la sortie de J2Wasm,
wasm-opta accéléré chaque benchmark, avec un gain moyen de 1,9×
Optimisations WasmGC dans V8
- Les langages à GC ont un modèle de performance différent de celui des langages comme C, C++ et Rust, qui s’appuient beaucoup sur l’inlining au moment de la compilation
- Les langages à GC comme Java et Dart s’appuient généralement sur la VM pour effectuer inlining et optimisations à l’exécution
- En Java, tous les appels commencent d’abord comme des appels indirects
- Les classes enfants peuvent surcharger les fonctions parentes, et il en va de même lorsqu’on appelle un enfant via une référence de type parent
- Il est bénéfique qu’une toolchain transforme des appels indirects en appels directs, mais le vrai code Java comporte beaucoup de chemins difficiles à inférer statiquement comme appels directs
- V8 implémente le speculative inlining pour WasmGC
- Il observe les appels indirects à l’exécution
- Si un site d’appel donné présente un comportement simple, comme un petit nombre de cibles d’appel, il effectue l’inlining avec un contrôle de garde
- Google Sheets Calc Engine est une base de code Java qui calcule les formules de feuilles de calcul, et elle était auparavant compilée en JavaScript avec J2CL
- L’équipe V8 a collaboré avec Sheets et J2CL pour porter ce code vers WasmGC
- Sur ce code, le speculative inlining a été l’optimisation unique implémentée par V8 pour WasmGC ayant eu le plus grand effet
- Avec environ 30 % d’accélération, il apporte une amélioration supérieure à toutes les autres optimisations mesurables combinées
- Les autres optimisations WasmGC de V8 incluent notamment load elimination, optimisations basées sur les types, branch elimination, constant folding, escape analysis et common subexpression elimination
- Les informations de types de WasmGC sont aussi utilisées pour les optimisations à l’exécution
- Si
ref.testréussit un test sur un type donné, alors unref.castvers ce même type doit réussir - Dans des motifs comme un downcast après
instanceofen Java, le cast peut être supprimé
- Si
- Avec WasmMVP, la séparation des rôles entre optimisations de la toolchain et de la VM était relativement claire, mais avec WasmGC, les caractéristiques des langages à GC et les possibilités d’optimisation de la représentation WasmGC peuvent faire davantage se chevaucher les optimisations de la toolchain et de la VM
État actuel et points de départ
- WasmGC a atteint la phase 4 au W3C et est devenu une norme complète et finalisée
- Chrome 119 inclut la prise en charge de WasmGC
- Firefox 120 devait sortir avec la prise en charge de WasmGC plus tard le même mois
- Dans la démo Flutter, Dart compilé avec WasmGC exécute la logique applicative, y compris les widgets, la mise en page et les animations
- Les toolchains prenant en charge WasmGC sont les suivantes
- Le petit exemple de la section sur les outils développeur est un exemple de programme WasmGC “hello world” écrit à la main, dont le code source est disponible
- On peut y voir la définition du type
$Nodeet la méthode de création avecstruct.new
- On peut y voir la définition du type
- Le wiki Binaryen traite de la façon d’émettre du code WasmGC facile à optimiser pour les compilateurs
- GC Implementation - Lowering Tips
- On peut aussi consulter les passes et flags Binaryen utilisés par les toolchains Java, Dart et Kotlin
1 commentaires
Avis de Hacker News
WASM ressemble à un exemple de taille fine (thin waist) : avec un ramasse-miettes en plus, on passe d’une structure N×M à N+M. Autrement dit, on peut aller vers N langages + M machines virtuelles + G ramasse-miettes, et V8 dispose déjà d’un ramasse-miettes mature.
Je me demandais s’il existait un outil pour passer de WASM à la JVM, et il y en avait un sur GitHub. Je ne l’ai pas essayé moi-même ; je l’ai cherché parce que la JVM dispose elle aussi de ramasse-miettes matures et parallèles.
Je ne pensais pas que WASMGC arriverait aussi vite ; désormais, j’attends aussi avec impatience les WASM Threads, pour un véritable parallélisme et pas seulement du parallélisme d’E/S.
Si l’on a l’occasion de résoudre efficacement l’asynchronisme, le parallélisme et le ramasse-miettes, WASM deviendra plus puissant et pourrait ne pas devenir une source de confusion ou de difficultés pour les développeurs. À mon avis, WASI est important aussi parce qu’il offre l’occasion de définir une API aussi stable que POSIX.
1: https://www.oilshell.org/blog/2022/02/diagrams.html
2: https://github.com/cretz/asmble
J’espère que les idées et explications sur la façon dont la représentation intermédiaire WASM a été mappée vers le bytecode JVM seront utiles à ceux qui réalisent une implémentation plus officielle. Il n’y a actuellement aucun projet de prise en charge de WASM GC.
« Plus de 20 éditeurs d’outils de programmation proposent environ 26 langages de programmation sur .NET, dont C++, Perl, Python, Java, COBOL, RPG et Haskell. »
Source : https://news.microsoft.com/2001/10/22/massive-industry-and-d...
Ce n’est pas tant parce que le monde académique serait particulièrement audacieux avec la JVM, mais plutôt parce que les programmes JVM étaient largement utilisés, et souvent d’une manière qui mettait sous pression une technique unique de ramasse-miettes.
[^1]: https://www.baeldung.com/jvm-garbage-collectors. La JVM d’Azul possède aussi son propre ramasse-miettes distinct. Ce ne sont que ceux que je connais, et j’en oublie probablement pas mal.
Je suppose que c’était en comparaison avec les coroutines, mais les coroutines sont séquentielles. Même si l’ordre d’exécution peut être arbitraire, on peut compter sur le fait qu’elles ne s’exécutent pas simultanément.
Si wasm adopte les threads, ce sera probablement encore une regrettable situation de type « Worse is Better ». Les threads sont parmi les pires modèles de concurrence que nous ayons inventés. Sauf, bien sûr, si l’on met de côté des choses comme concurrent COMEFROM.
Les outils WASM pour Julia peuvent créer des applications qui prennent en charge cette fonctionnalité ou en ont besoin. Par exemple, on trouve un exemple de compilation d’un solveur d’ODE en WASM sur https://tshort.github.io/WebAssemblyCompiler.jl/stable/examp...
Pour que cela fonctionne directement, il faut Chrome v119, car c’est la première version qui a activé la prise en charge du ramasse-miettes. La page d’accueil du compilateur WASM https://tshort.github.io/WebAssemblyCompiler.jl/stable/ donne plus de détails.
J’espère que le nouveau type
Memory{T}permettra de compiler davantage de code.Cela donne l’impression que « wasm est le nouveau LLVM ». Je me demande si d’autres ressentent la même chose
Je comprends ce que l’on essaie de faire, et j’ai vu des démonstrations qui en montrent la puissance, mais dans la plupart des cas cela reste techniquement très bas niveau, et devient souvent assez pénible à utiliser concrètement
Je me demande quand on estimera que c’est prêt à être adopté plus largement au point d’être choisi comme cible plutôt que l’approche actuelle
Le nom entraîne une confusion fréquente : LLVM visait au départ une machine virtuelle, mais ne l’a jamais vraiment été, et ne l’est toujours pas. La première phrase de la page d’accueil le précise clairement : https://llvm.org/
En gros, c’est un ensemble de bibliothèques C++ qui implémentent une représentation intermédiaire évoluant avec le temps, et des outils pour aider à écrire des compilateurs
Il y a une dizaine d’années, Google avait un projet visant un domaine proche de WASM, et je pense qu’ils faisaient cette confusion. Ils considéraient LLVM comme une machine virtuelle. C’était probablement quelque chose comme PNaCl
C’est un peu comparable à la façon dont LuaJIT a figé Lua sur Lua 5.1. Lua n’était pas une norme, mais on peut figer une version précise pour la réimplémenter. Cela dit, cette approche a un problème évident : celui qui réimplémente ne connaît pas tous les bugs qu’il fige aussi dans le temps
Certains « compromis » de WASM m’ont déjà fait hausser un sourcil, mais une chose est indéniable : WASM est réellement une machine virtuelle
J’ai vu une présentation sur WASM GC, et ses auteurs parlaient franchement des compromis. Par exemple, au début, des casts à l’exécution sont nécessaires, et le surcoût mesuré reste raisonnable. Cette attitude inspire plutôt confiance
https://old.reddit.com/r/ProgrammingLanguages/comments/17crk...
Honnêtement, je ne comprends pas bien wasm. Je ne vois pas clairement quel problème précis nous essayons de résoudre
Je me demande si l’objectif est d’écrire des applications dans des langages autres que JavaScript, ou que du code transpilé vers JavaScript. Je ne sais pas si cela inclut l’interaction avec la couche d’affichage du DOM dans le navigateur. D’après ce que j’en comprends, on ne peut pas vraiment le faire ; si c’est bien le cas, je me demande si l’objectif est alors d’exécuter les applications dans un canevas dédié à l’intérieur du DOM, à la manière de Flash. Et dans ce cas, je ne sais pas à quel point cette niche est grande
Dans un autre fil, il était aussi question d’utiliser wasm avec Cloudflare Workers. Peut-être parce que cela fonctionne via l’interpréteur JavaScript. Mais si les gens veulent écrire des Cloudflare Workers avec des runtimes arbitraires, il me semble que Cloudflare pourrait simplement ajouter la prise en charge d’autres runtimes. Ils en ont probablement les moyens
Ou alors, indépendamment du lien avec l’écosystème JavaScript, je me demande si wasm a quelque chose d’intrinsèquement excellent en tant que bytecode
J’ai l’impression d’avoir raté le document d’introduction qui explique pourquoi cette technologie est intéressante, et à ce stade je suis un peu gêné de poser la question
LLVM ne fournit pas « par défaut un bac à sable de sécurité simple » sans beaucoup de code supplémentaire. wasmer a bien un backend LLVM, mais à titre personnel, c’est pour moi le plus grand facteur de différenciation de WASM
Cela va dans la même direction que LLVM, mais on en est encore loin
Aussi formidable que ce soit, et même si cela ouvre énormément de possibilités, je n’arrête pas de penser au fait que les navigateurs d’aujourd’hui sont extrêmement complexes et que la barrière à l’entrée pour en créer un soi-même est presque trop raide
Je ne vois pas pourquoi la plateforme hypermédia la plus réussie et la plus accessible au monde devrait être facile à implémenter. Évidemment que le système en ligne le plus prospère au monde est complexe et riche en fonctionnalités. C’est pour cela que nous l’utilisons, et c’est pour cela qu’il a gagné
Il est impressionnant que cela ait fini par être créé et publié. On entendait parler depuis des années du projet d’ajouter un GC à WASM, et je n’étais pas sûr que cela arriverait vraiment.
Je me demande dans quelle mesure cela aidera les langages ciblant WASM qui ont le problème de binaires volumineux parce qu’ils embarquent un runtime. Si je me souviens bien, Blazor nécessitait environ 1 Mo rien que pour un hello world, et je ne sais pas si WasmGC y changera quelque chose.
Bien sûr, ce qui m’inquiète, c’est que si ce sont des fonctionnalités dont seul .NET a besoin, l’incitation à les ajouter ne sera pas très forte. À ce stade, une fonctionnalité de type
include, permettant de mettre en cache une version donnée du GC et de la faire charger par d’autres assemblies WASM, pourrait être plus utile, même si elle apporte son lot de difficultés.[0] - https://github.com/WebAssembly/gc/issues/77
C et Rust n’incluent pas de garbage collector, mais doivent tout de même embarquer malloc/free pour gérer la mémoire. Si Java est plus petit ici, c’est parce qu’il n’a pas du tout besoin d’embarquer de code de gestion mémoire.
https://developer.chrome.com/blog/wasmgc/
Dans le cas de Blazor, cela n’aidera probablement que pour le GC. Si je me souviens bien, Blazor doit envoyer tout le runtime dotnet avec l’application.
Le nouveau support wasm de Kotlin est assez prometteur. Il existe une version expérimentale de Compose Multiplatform pouvant cibler le navigateur, et elle devrait utiliser WASM.
Compose Multiplatform, c’est essentiellement Google Jetpack Compose pour Android avec la prise en charge d’autres plateformes en plus.
Il y a quelques jours, le support iOS était en alpha et devrait passer en bêta l’an prochain. Le support Android et desktop est désormais stable. Une fois tout cela stabilisé, on pourra en pratique écrire des applications UI fonctionnant sur presque n’importe quelle plateforme.
Le compilateur wasm devrait sortir avec la prochaine version majeure de Kotlin, Kotlin 2.0, qui inclura le nouveau compilateur k2. Cela semble possible vers le début de l’année prochaine. k2 est actuellement disponible en bêta et peut être activé dans Kotlin 1.9.x.
L’avantage de l’écosystème Kotlin multiplateforme, c’est qu’il existe déjà beaucoup de bibliothèques fonctionnant sur plusieurs plateformes. Le compilateur wasm devrait donc rapidement en faire partie et hériter de bonnes bibliothèques. En général, il suffit d’adapter la configuration de build pour cibler cet écosystème, puis d’implémenter les comportements spécifiques à une plateforme qui manquent.
Un autre point intéressant dans ce domaine est l’utilisation et l’interconnexion de bibliothèques écrites dans des langages différents. Par exemple, beaucoup de parties spécifiques aux plateformes dépendront probablement de bibliothèques C ou Rust déjà existantes. Dans de nombreux cas, il pourrait même s’agir des mêmes bibliothèques que celles utilisées par Kotlin Native.
Je me demande aussi s’il utilise les mêmes widgets sur desktop, web, iOS et Android. Je comprends que sur Android, c’est natif. Sur les autres plateformes, je ne sais pas s’il imite différents styles. La vraie question, c’est le ressenti d’une application Compose Multiplatform sur les plateformes non Android.
Par exemple, Dart sur le web me donne personnellement une impression de fortes saccades, et Electron sur desktop aussi. Dart sur desktop est correct, mais les applications ont généralement de gros boutons et beaucoup d’espacements, ce qui les fait davantage ressembler à des apps Android tournant dans un émulateur qu’à de vraies applications natives.
Je me demande aussi si Kotlin est un langage que l’on peut recommander pour de nouveaux projets.
Je me demande si quelqu’un peut expliquer pourquoi cet article de blog et l’annonce de Chrome ne mentionnent pas Go.
Go utilise aussi le garbage collection, ce qui donne l’impression qu’il ne pourra pas bénéficier de ce changement.
Je me demande s’il serait pertinent que le runtime expose une bibliothèque d’allocation intégrée utilisable par C/C++/Rust
Les programmes pourraient choisir cette bibliothèque au lieu d’embarquer leur propre bibliothèque d’allocation
Les primitives de Wasm GC ont été conçues pour obtenir le plus grand gain avec l’ajout le plus réduit possible. Lorsque d’autres primitives comme SIMD sont ajoutées, c’est aussi parce qu’elles constituent « à peu près les primitives minimales » nécessaires pour fournir la fonctionnalité. Ici, il faut les plus petits blocs de construction possibles
Mais une API d’allocateur mémoire comme malloc/free n’est pas une fonctionnalité minimale. Un allocateur mémoire est en réalité un ensemble de politiques et de décisions de conception, dont découlent ensuite les algorithmes. Le bloc de construction fondamental dans ce domaine est un « morceau de mémoire linéaire possédé par l’allocateur », et c’est la mémoire linéaire que WASM possède depuis le premier jour
Il peut être pertinent que certains runtimes le fournissent individuellement. Par exemple, en passant une partie de la mémoire linéaire à un import wasm intégré, afin qu’un allocateur implémenté par le runtime s’en charge. Mais je pense qu’il y a très peu de chances que ce soit standardisé. De plus, la plupart des outils pour les langages Wasm sans GC sont conçus pour porter des bases de code existantes et leurs allocateurs. Au final, cela revient à augmenter la quantité de code à maintenir pour une implémentation ponctuelle, et l’effort pourrait ne pas en valoir la peine
S’il n’est pas déterministe, les différences de comportement des programmes pourraient devenir importantes. Par exemple, si un navigateur intègre un allocateur « intelligent » qui réutilise mieux la mémoire, un programme qui échouerait par manque de mémoire dans un autre navigateur à cause de la fragmentation de la mémoire linéaire pourrait réussir dans celui-là. Historiquement, les navigateurs ont fait de grands efforts pour standardiser les comportements afin d’éviter ce genre de différences
Standardiser le comportement d’une implémentation de malloc/free est également délicat pour plusieurs raisons. Premièrement, la complexité est élevée : gestion de la free list, tailles de chunks utilisées, etc. C’est beaucoup à mettre dans une spécification. Deuxièmement, standardiser un comportement précis empêcherait de l’améliorer, alors que de meilleures idées pour malloc/free continuent encore d’apparaître
Pour ces raisons, je suis sceptique quant à la possibilité que cela entre dans la spécification wasm
En revanche, le GC évite ces problèmes. Comme les valeurs de pointeurs ne sont pas observables, il n’y a rien à spécifier
allocet les fonctions associées comme fonctions du runtime, pour que le code traditionnel les appelle, et n’importe quel GC pourrait être construit par-dessusJe suis assez sceptique vis-à-vis de cette direction. Elle augmente fortement la complexité de WebAssembly.
Un garbage collector est une abstraction qui fuit. Certains prennent en charge les pointeurs internes, d’autres non. Certains prennent en charge les traitements parallèles qui partagent la mémoire, d’autres non. Certains nécessitent de la compaction, ce qui rend la FFI C plus difficile, d’autres non. Certains doivent être profondément intégrés aux mécanismes utilisés pour les processus/threads verts et les piles extensibles, d’autres non.
Si l’on regarde des langages comme Erlang, JavaScript, Python ou Go, les choix faits au niveau du langage se reflètent dans une certaine mesure dans le garbage collector.
L’idée d’une machine virtuelle universelle/générique prenant en charge plusieurs langages a été tentée plusieurs fois avec la JVM, le CLR, Parrot, etc., avec un succès limité. Je me demande ce qui est différent cette fois-ci.
La JVM/le CLR ont d’abord créé une machine virtuelle, une spécification d’instructions en langage intermédiaire et un runtime, puis ont essayé de les déployer aussi largement que possible en espérant une adoption universelle côté client. Autrement dit, ils pariaient que la « VM de langage intermédiaire » serait tellement attrayante qu’elle se diffuserait dans toute l’industrie. Cette attente ne s’est vérifiée qu’en partie. La JVM/le CLR se sont diffusés sur le desktop et les serveurs, mais, dans les navigateurs web, les Java Applets et Microsoft Silverlight ont échoué, et ils n’ont pas non plus été largement adoptés sur les plateformes mobiles.
WASM procède dans l’ordre inverse. Il part à rebours de ce qui est déjà déployé et adopté dans toute l’industrie, à savoir JavaScript, pour créer une « machine virtuelle, une spécification d’instructions en langage intermédiaire et un runtime ».
De ce point de vue, le prétendu « langage jouet » qu’est JavaScript a été un cheval de Troie de 20 ans pour se diffuser d’abord largement sur tous les clients. Maintenant, l’industrie semble dire : « Quelqu’un a-t-il remarqué qu’il existe déjà un runtime universel, JavaScript, sur serveur + desktop + navigateur + mobile ? Créons un runtime de langage intermédiaire pour le rendre rapide. »
Il y avait aussi quelques problèmes techniques. La JVM de Sun n’avait pas de pointeurs bruts, ce qui en faisait difficilement une cible performante pour des langages à pointeurs comme C/C++, et le CLR de Microsoft n’était pas disponible sur macOS. Le CLR minimal de Silverlight fait exception. Mais, plus que ces limites techniques, l’ordre dans lequel JavaScript s’est déployé comme un cheval de Troie inoffensif explique davantage la situation.
Wasm est de plus bas niveau que la plupart des formats de bytecode mentionnés plus haut. Cela reste vrai même avec Wasm GC, qui ajoute des structures et des tableaux typés statiquement, et avec la proposition function-references, qui apporte des fonctions typées. Wasm GC prend aussi explicitement en charge les pointeurs tagués
i31ref.Pour ces raisons, Wasm GC est plus général que les tentatives citées plus haut, précisément parce qu’il est de plus bas niveau.
Créer une VM « universelle » ou « générique » qui prendrait en charge plusieurs langages de manière égale et transparente n’est pas un objectif explicite de WASM-GC. L’idée est que chaque implémentation pourra avoir besoin de ses propres bricolages et sémantiques spécifiques au-dessus du support de base fourni par WASM, et que ce n’est pas un problème, car la FFI et l’interopérabilité entre langages sont considérées comme un sujet totalement distinct.
Le garbage collection de JavaScript restera là, et il semble être le plus durablement installé parmi les exemples mentionnés.
Dans de nombreux cas, même s’il est inférieur, d’autres langages et applications/bibliothèques devront s’y adapter.
wasm fournit des fonctionnalités de GC afin que les langages à runtime à mémoire managée puissent cibler l’environnement wasm sans souffrir des limites d’un modèle mémoire restreint, et, accessoirement, pour réduire la taille des bundles.
wasm pourrait prendre en charge des paramètres de GC plus ajustables afin de mieux correspondre aux caractéristiques du langage invité. De plus, contrairement aux runtimes de langages généralistes auxquels on le compare, les implémenteurs de langages n’ont tout simplement pas la possibilité de créer un runtime entièrement personnalisé.
Plusieurs préoccupations concernant la faisabilité actuelle de WASM GC sont abordées ici. Version anglaise via Google Traduction :
https://habr-com.translate.goog/ru/articles/757182/?_x_tr_sl...
Original :
https://habr.com/ru/articles/757182/
C’est un article de l’auteur de TeaVM, qui travaille depuis 10 ans à faire tourner efficacement du code Java et JVM dans le navigateur. https://teavm.org/
La conversion Java-vers-JavaScript existante de TeaVM offre de bonnes performances tout en utilisant le GC JS du navigateur. Il sera intéressant de voir si WASM GC, une fois plus mature, permettra d’aller encore plus vite
Pour laisser quelques notes sur les problèmes abordés dans l’article : le besoin d’une pile fantôme manuelle est résolu par WasmGC. Comme le dit le lien, c’est la même approche que celle qui fonctionne en JS
L’absence de try-catch est déjà résolue par la proposition de gestion des exceptions Wasm, déjà déployée dans les navigateurs : https://github.com/WebAssembly/exception-handling/blob/main/...
Les vérifications de null sont elles aussi en grande partie résolues par WasmGC. La spécification définit des types locaux non nullables, et la VM peut optimiser à l’aide de signaux, comme avec la technique mentionnée dans l’article. Par exemple, Wizard procède ainsi
L’initialisation des classes est, comme le dit l’article, un problème difficile. J2Wasm et Binaryen travaillent à l’optimiser par analyse statique au niveau de la chaîne d’outils. Une PR que j’ai récemment écrite a fait avancer les choses dans cette direction : https://github.com/WebAssembly/binaryen/pull/6061
Le surcoût des vtables mentionné dans l’article peut poser problème. Cela dit, je ne connais pas encore de bonnes mesures. Il existe quelques idées de solutions post-MVP pour la répartition des méthodes, mais encore rien de concret
Concernant les vérifications de null et les traps, il y a eu des discussions sur des variantes d’instructions GC qui lèveraient des exceptions au lieu de provoquer un trap. Mais comme les mesures ne montrent pas aujourd’hui de gros problème, ce n’est pas prioritaire
L’auteur a raison de dire que le parcours de pile, les signaux et le contrôle de la mémoire sont des domaines importants
Globalement, grâce à WasmGC et à la gestion des exceptions, le Java généré aujourd’hui par J2Wasm est plutôt bien placé. Il est généralement plus rapide que J2CL, qui compile Java en JavaScript. Il reste toutefois clairement de la marge pour s’améliorer