Le 28 février, les quatre navigateurs principaux ont annoncé leur consensus sur le fait que WebAssembly était suffisamment avancé pour fournir un produit viable. Ceci fournit une version initiale stable que les navigateurs peuvent implémenter et mettre à disposition.
Ceci fournit un noyau stable que les navigateurs peuvent rendre disponible. Ce noyau ne contient pas toutes les fonctionnalités prévues par le groupe communautaire, mais il en contient suffisamment pour que WebAssembly soit rapide et utilisable.
Avec ceci, les développeurs peuvent commencer à diffuser du code WebAssembly. Pour les versions antérieures des navigateurs, les développeurs peuvent fournir une version asm.js du code. asm.js étant un sous-ensemble de JavaScript, tout moteur JavaScript pourra exécuter ce code. Avec Emscripten, vous pouvez compiler la même application vers WebAssembly et vers asm.js
Même dans cette version initiale, WebAssembly sera rapide. Il devrait devenir plus rapide à l’avenir, grâce à un ensemble de corrections et de nouvelles fonctionnalités.
Améliorer les performances de WebAssembly dans les navigateurs
Certaines améliorations de vitesse viendront au fur et à mesure que les navigateurs amélioreront la prise en charge de WebAssembly dans leurs moteurs. Les fournisseurs de navigateurs travaillent sur ces différents problèmes de manière indépendante.
Des appels de fonction plus rapides entre JavaScript et WebAssembly
Actuellement, appeler une fonction WebAssembly dans du code JavaScript est plus lent que ce qu’on pourrait espérer. C’est à cause de ce qu’on appelle le « trampolinage ». Le compilateur JIT ne sait pas comment interagir directement avec WebAssembly et il doit donc rediriger le code WebAssembly vers quelque chose qui sait le faire. Le composant en question est un code lent dans le moteur et qui prépare à exécuter le code WebAssembly optimisé.
Cette étape peut être jusqu’à 100 fois plus lente que ce qu’on aurait obtenu si le compilateur JIT savait comment l’interpréter directement.
Vous ne remarquez pas ce délai si vous passez une seule tâche conséquente au module WebAssembly. En revanche, si vous avez de nombreux allers-retours entre WebAssembly et JavaScript (comme lorsqu’on effectue de petites tâches), ce délai sera remarquable.
Un temps de chargement plus rapide
Les compilateurs à la volée (JIT) doivent négocier un compromis entre des temps de chargement plus rapides et des temps d’exécution plus rapides. Si on passe plus de temps à compiler et optimiser le code en amont, cela accélèrera l’exécution mais ralentira le démarrage du programme.
De nombreux travaux sont en cours pour améliorer cet équilibre entre la compilation en amont (qui s’assure qu’il n’y a pas de ralentissement lorsque le code a démarré son exécution) et l’hypothèse simple comme quoi la plupart du code ne sera pas exécuté suffisamment pour que l’optimisation soit rentable.
Puisque WebAssembly n’a pas besoin de spéculer sur les types qui seront utilisés, les moteurs n’ont pas à surveiller les types manipulés lors de l’exécution. Délestés de cette tâche, ils peuvent faire autre chose et notamment compiler et exécuter le code en parallèle.
De plus, de récents ajouts à l’API JavaScript permettront d’effectuer une compilation au fil de l’eau (en utilisant des streams). Cela signifie que le moteur pourra commencer la compilation alors que le module est toujours en cours de téléchargement.
Dans Firefox, nous travaillons sur un système à deux compilateurs. Un premier compilateur est exécuté en amont et effectue une première optimisation du code, plutôt efficace. Pendant que ce code est exécuté, un deuxième compilateur effectue une optimisation poussée en arrière-plan. Lorsque la version pleinement optimisée est disponible, on bascule l’exécution sur cette version.
Ajouter des fonctionnalités à la spécification après cette phase initiale
Un des objectifs de WebAssembly est de construire la spécification au fur et à mesure, par petits morceaux, plutôt que de concevoir tout d’un bloc en amont.
Cela signifie qu’on attend de nombreuses fonctionnalités mais qu’elles n’ont pas encore été complètement conçues. Elles devront passer par une phase de spécification dans laquelle interviennent tous les fournisseurs de navigateur.
Ces fonctionnalités sont intitulées « fonctionnalités futures ». En voici quelques-unes.
Manipuler le DOM directement
Actuellement, il n’existe aucun moyen qui permette d’interagir avec le DOM. Cela signifie que depuis WebAssembly, on ne peut pas utiliser un objet comme element.innerHTML
pour mettre à jour un nœud du document.
En l’état, il faut passer par JavaScript pour définir la valeur. Cela signifie qu’il faut transmettre une valeur au code JavaScript qui a appelé le module WebAssembly ou qu’il faut appeler une fonction JavaScript depuis le code WebAssembly (un module WebAssembly pouvant importer des fonctions WebAssembly et des fonctions JavaScript).
Dans tous les cas, utiliser JavaScript comme intermédiaire sera plus lent qu’un accès direct. Certains champs d’application de WebAssembly devront peut-être attendre que ce point soit résolu.
Un accès concurrent à la mémoire partagée
Une méthode pour accélérer le code consiste à exécuter différentes parties du code en même temps, en parallèle. Cette approche réserve parfois de mauvaises surprises, car la communication entre les threads peut nécessiter plus de temps qu’il n’aurait fallu pour exécuter la même tâche de façon classique.
Mais lorsqu’il est possible de partager la mémoire entre les threads, ce délai est réduit. Pour cela, WebAssembly utilisera les nouveaux objets SharedArrayBuffer
de JavaScript. Une fois que ce type d’objet sera implémenté dans les navigateurs, le groupe de travail pourra définir la façon dont WebAssembly fonctionne avec SharedArrayBuffer
.
SIMD
Si vous avez lu d’autres billets ou regardé des présentations sur WebAssembly, vous avez pu entendre parler de SIMD. Cet acronyme signifie Single Instruction Multiple Data (NDT pour « instruction unique, données multiples »). C’est une autre méthode pour exécuter des tâches en parallèle.
SIMD permet de traiter de grands ensembles de données (un vecteur de différents nombres par exemple) et d’appliquer en même temps la même instruction aux différentes parties de cet ensemble. Grâce à cet outil, on peut accélérer de façon drastique certains calculs complexes nécessaires pour les jeux ou la réalité virtuelle.
Cette avancée n’est pas primordiale pour les développeurs d’applications web classiques, mais elle est cruciale pour les développeurs qui travaillent sur des applications multimédia comme les jeux vidéos.
La gestion des exceptions
De nombreuses bases de code écrites dans des langages tels que C++ utilisent les exceptions. Cependant, les exceptions ne font pas encore partie de la spécification WebAssembly.
Si vous compilez votre code avec Emscripten, celui-ci émulera la gestion des exceptions pour certains niveaux d’optimisation. Cependant, cette émulation reste plutôt lente et si vous souhaitez la désactiver, vous pouvez utiliser l’option de compilation DISABLE_EXCEPTION_CATCHING
.
Lorsque les exceptions seront gérées nativement par WebAssembly, cette émulation ne sera plus nécessaire.
D’autres pistes d’amélioration : simplifier le travail des développeurs.
Certaines fonctionnalités à venir n’auront pas d’impact sur les performances mais faciliteront la tâche aux développeurs qui travaillent avec WebAssembly :
- Des outils de développement de premier rang pour interagir avec le code source. À l’heure actuelle, déboguer du code WebAssembly dans le navigateur ressemble un peu à déboguer de l’assembleur brut. Très peu de développeurs peuvent faire le lien mental entre le code source et l’assembleur obtenu. Nous cherchons à améliorer les outils disponibles afin que les développeurs puissent déboguer leur code source.
- Intégration du ramasse-miettes. Si vous pouvez définir les types en amont, vous devriez pouvoir transformer votre code en WebAssembly. Du code écrit avec un langage tel que TypeScript devrait donc être compilable en WebAssembly. Le seuil écueil qui subsiste est que WebAssembly ne sait pas comment interagir avec les ramasse-miettes existants tels que ceux construits dans les moteurs JavaScript. L’idée de cette fonctionnalité est de permettre à WebAssembly d’accéder au ramasse-miettes natif grâce à un ensemble de types et d’opérations de bas niveau qui sont reliées au ramasse-miettes.
- Intégration des modules ES6. Les navigateurs sont en train d’ajouter la prise en charge du chargement des modules JavaScript grâce à la balise
script
. Une fois cette fonctionnalité ajoutée, une balise comme<script src=url type="module">
fonctionnera, même si l’URL pointe vers un module WebAssembly.
Conclusion
WebAssembly est rapide aujourd’hui et avec les nouvelles fonctionnalités et améliorations des implémentations dans les navigateurs, il devrait devenir encore plus rapide
À propos de Lin Clark
Lin est ingénieure au sein de l’équipe Mozilla Developer Relations. Elle bidouille avec JavaScript, WebAssembly, Rust et Servo et crée des bandes dessinées sur le code.