Le web gagne de plus en plus en complexité, en effet aujourd’hui la plupart des sites web tendent à utiliser des technologies coûteuses en ressources : réalité virtuelle, intégration 3D, algorithmes de recherche, etc.

Genèse

Pour répondre à cette demande, en 2015, Google et asm.js annoncèrent l’arrivée prochaine d’une technologie innovante capable de s’allier à Javascript pour optimiser la vitesse d’exécution de certains processus coûteux, je parle bien entendu de WebAssembly. WebAssembly est un nouveau langage de bas niveau de type assembleur, formaté en bytecode (instructions proches du langage machine), exécutable dans les navigateurs modernes le supportant (Chrome, Edge, Mozilla Firefox and Safari) et se voulant aussi performants que des langages de bas niveaux tels que C, C++ ou Rust. 

En quoi ça consiste ?

Le développeur écrit son sous-programme en C au lieu de l’écrire directement en Javascript, puis le compilateur va se charger de transformer directement son code C en bytecode WebAssembly et de générer le code Javascript permettant d’appeler l’API WebAssembly / JS.

L’API WebAssembly de Javascript va permettre à Javascript de charger les modules WebAssembly et inversement. La machine virtuelle de votre application web qui exécutait auparavant seulement du code Javascript va aujourd’hui également pouvoir charger et exécuter du code WebAssembly.

Pourquoi s’en soucier ?

Il est vrai que Javascript est facile d’utilisation et très “malléable” car c’est un langage de très haut niveau, de plus il est déjà le socle principal de la quasi totalité du web d’aujourd’hui… sauf qu’il est lent. Mais très lent. Le niveau d’abstraction que nous offre Javascript, son garbage collector, ainsi que bon nombre de fonctionnalités, le ralentissent et c’est pour cela que parfois, sur certains programmes, on aurait envie de se passer de Javascript et d’avoir sous la main un autre langage, plus bas niveau, sans toute cette couche abstraite qui nous est cachée et qui nous freine tellement.

C’est justement ce que nous offre WebAssembly : garder la puissance d’abstraction de Javascript et l’incontournable lien qu’il permet d’établir entre une page HTML / CSS et du code source, et déléguer des tâches coûteuses en ressources à des sous programmes écrits dans un langage de plus bas niveau, comme le C, le C++ ou le Rust (seuls ces trois là sont proposés pour l’instant) afin d’optimiser les performances de calcul à des endroits ciblés.

Intégrer WebAssembly dans Angular

Passons aux choses sérieuses, WebAssembly n’en est encore qu’à ses balbutiements et l’intégrer à Angular n’est pas une mince affaire.

Ici nous créons un nouveau projet Angular grâce à CLI

Nous avons envie que TypeScript puisse reconnaître des modules provenant de WebAssembly

Vous devez maintenant installer emsdk, emcc, et emscripten, voici quelques liens qui devraient vous être utiles :

L’installation terminée, nous allons pouvoir créer nos programmes en C qui seront ensuite compilés en WebAssembly par emcc, ce dernier va également générer le fichier Javascript associé au fichier WASM qui servira de lien entre le code source WASM et le TypeScript final afin de pouvoir être importé dans notre projet Angular.

Créez un dossier wasm dans app/

Dans ce fichier, nous avons deux fonctions qui sont là pour tester les performances de WebAssembly. Ces fonctions seront réécrites en TypeScript par la suite afin d’évaluer les différences de performances entre les deux technologies.

Nous allons à présent devoir compiler séparément evaluator.c, en effet je n’ai pas trouvé le moyen d’automatiser cette étape et elle est nécessaire pour obtenir notre .wasm et notre .js

Nous obtenons ainsi notre fichier binaire evaluator.wasm et notre fichier Javascript evaluator.js.

On remarquera l’utilisation de l’option MODULARIZE qui permet de rendre nos fonctions modulaires, ce qui facilite leur intégration dans Angular.

Maintenant que tout est fin prêt, créons un dossier service dans lequel nous allons demander à CLI de nous générer un nouveau service qui nous permettra de nous servir de nos toutes nouvelles fonctions.

Il faut également l’ajouter aux providers de l’app

Il ne vous reste qu’une chose à faire, appeler ce service qui appellera pour vous les fonctions Web Assembly. Pour ce faire vous pouvez, dans n’importe quel component de votre app, instancier une instance WasmService et appeler l’une de ses méthodes.

Architecture finale :

Et les performances dans tout ça ?

On aurait tendance à penser que toute cette couche ajoutée qui nous permet d’intégrer correctement notre code WebAssembly en Angular ralentirait considérablement l’exécution de nos fonctions, et… c’est en partie vrai.

 

Résultats pour fibonacci

 

En effet, pour appeler nos fonctions, on est obligé d’utiliser des requêtes asynchrones, ce qui implique une gestion d’événements, ce que la bibliothèque rxjs fait très bien à notre place, mais cela ralentit le code en appel et en sortie de programme.

Ce qui fait que sur de petits programmes, comme c’est le cas de Fibonacci, l’exécution du programme est trop courte pour compenser la perte de performances due à l’appel et à la sortie d’exécution. C’est pourquoi, il n’est pas judicieux d’intégrer WebAssembly à tout projet Angular, dans de nombreux cas, cela peut même s’avérer contre-productif.

En revanche, si vos programmes demandent un temps d’exécution assez conséquent, WebAssembly intégré à Angular peut s’avérer très performant :

 

Résultats pour playWithMemory

Conclusion

WebAssembly n’en est aujourd’hui qu’à ses débuts, les tests effectués dans le cadre de cet article restent minimes et des performances réellements impressionnantes peuvent êtres atteintes en calcul 3D (voir l’article de Kamaron Peterson). Son intégration à Angular est plutôt complexe et peu performante dans le cadre d’un développement front peu coûteux en ressources, donc à moins de devoir ré-afficher la fractale de Mandelbrot à chaque page de votre app, vous pouvez passer votre chemin.

 

Notes: vous pouvez cloner le projet de benchmark réalisé ici pour avoir une meilleure idée des performances entre WebAssembly et TypeScript ou simplement démarrer sur un petit projet clefs en main.