WebAssembly / Wasm pour plus de puissance sur le web ?

Présenté en 2015 par Brendan Eich, le créateur de JavaScript et en partie d’Asm.js, WebAssembly ou wasm pour les intimes, est un nouveau langage de représentation intermédiaire (IRL) compatible avec tous les navigateurs.

WebAssembly est au web ce que l’assembleur est à votre ordinateur.  Les plus anciens d’entre-nous connaissent bien l’assembleur car à l’époque, c’était la seule façon d’étendre les capacités de nos librairies standards de programmation. Et puis, c’était aussi la garantie (à condition de ne pas coder avec les pieds) d’avoir des fonctions ultra-rapides et dont nous avons la parfaite maitrise ! Wasm, c’est exactement ça !

Dans cet article, je vous propose de reparler un peu d’assembleur. Puis je vous présenterai Wasm, son fonctionnement, son intégration à Emscripten. Et enfin, nous finirons par des petits tests à la “hello world”, mais en WASM et en WAST.

Qu’est-ce que l’assembleur ?

Pour résumer, on prend un script composé d’un ensemble d’instructions relativement limité et adapté à un type particulier de processeur, et on l’assemble pour former un code binaire que le processeur comprend directement. Selon les besoins, on le “linkera” avec divers librairies, écrites ou non en assembleur.

Exécuter un code binaire consiste à “brancher” directement le processeur dans une zone mémoire grâce à son pointeur d’instructions. Cette zone contient notre binaire chargé par le système d’exploitation. Ainsi, le processeur va interpréter une série de “A0 0F 23 5E…” comme autant d’instructions qu’il reconnait (commençant par un opcode). Il n’y a rien de plus proche de la machine que ce langage dit “binaire” car il s’agit ni plus ni moins qu’une succession de 0 et de 1 à son niveau.

Un programme écrit dans un langage dit “de haut niveau” (car plus éloigné de la machine) nécessite d’être interprété ou compilé. La compilation consiste à le transformer dans un langage intermédiaire, voir en assembleur lorsqu’on cible un processeur particulier. De par sa nature “haut niveau”, il est aussi beaucoup plus portable.

Personne (à ma connaissance) ne sait coder directement en binaire, ou alors pour de tout petits programmes, pour déboguer un driver, etc. Mais cette forme de programmation n’est absolument pas adapté à l’être humain.Il faudrait mémoriser tous les opcodes, penser les variables en terme de zone mémoire, etc. C’est quasiment impossible pour un être normalement constitué.

Par contre, l’assembleur est vraiment très simple d’accès. Pour moi, il est bien plus facile d’apprendre à programmer en assembleur qu’en tout autre langage (Bon, plus facile sur un RISC qui a un nombre d’instructions limité que sur un CISC comme la majorité de nos PC et pour lesquels le jeu d’instruction est de plus en plus important).

La difficulté quand on est proche de la machine, c’est qu’il est aussi plus long, plus difficile de créer des programmes importants. Et ces derniers sont bien plus difficile à maintenir dans le temps. D’où l’intérêt de mixer des langages de haut niveau comme le C++ avec des librairies écrites en assembleur pour les parties les plus sensibles, celles qui demandent le plus d’optimisations.

Maintenant, si vous n’êtes pas un “codeur assembleur”, je ne vais pas vous conseiller ce terrain. Il faut savoir que les compilateurs modernes sont tellement optimisés qu’il est très souvent difficile de faire mieux. J’avais créé une librairie il y a quelques années en assembleur pour exploiter les instructions SIMD des processeurs AMD64 (SSE), et assez rapidement l’option est apparue sous GCC (Compilateur Gnu C). Au final, le compilateur savait exploiter de lui-même ces nouvelles instructions et mes routines assembleur n’apportaient quasiment plus rien.  Sauf qu’un programme contenant une partie de son code en assembleur perd beaucoup en portabilité. Si vous écrivez un programme en C en utilisant la librairie standard, vous pourrez le compiler pour votre PC (x64), votre Raspberry, votre mobile android (ARM), votre Atari (m68000), votre CPC (z80)… Mais s’il est en assembleur, il vous faudra réécrire le code pour chaque processeur car les instructions ne sont pas les mêmes.

Si vous n’êtes pas familiarisé avec l’assembleur, je vous propose de parcourir rapidement cet article sur l’assembleur x64 et de consulter cette page.

Qu’est-que que Wasm ?

C’est un code binaire  qui est chargé directement par le navigateur. Bien entendu, il ne sera pas lu directement par le processeur de votre ordinateur, mais par une machine virtuelle (comme le bytecode et Java). Et grâce à cette machine virtuelle intermédiaire, vous n’avez plus de soucis de portabilité. Un seul code, une MV / processeur.

A quoi cela sert-il ? On avait déjà la compilation Just-In-time permettant de traduire des scripts à la volée pour qu’ils soient exécutés rapidement par votre ordinateur. L’intérêt est ici qu’il n’y a pas de temps de compilation intermédiaire (bon, y-a une machine virtuelle quand même), que le code binaire est beaucoup plus petit et qu’il charge donc plus rapidement, et qu’on peut ainsi protéger son code !

Vous avez déjà utilisé un moteur 3D pour le web codé en Javascript ? Souvent, la seule protection consiste à rendre illisible le code en supprimant les blancs inutiles à la compilation et les commentaires… mais le code reste lisible – c’est du javascript. Là, allez lire du WASM. Ca reste possible… tout comme on peut décompiler un programme binaire… mais c’est vraiment pas ragoutant.

WASM est Open Source, sous licence Apache. Vous pouvez aller jeter un oeil à son dépôt Github.

Wasm est conçu sous standard ouvert W3C (comprend des représentants de tous les principaux navigateurs). Tout a été pensé pour qu’il soit plus rapide à analyser et à exécuter que javascript. Ainsi, il est possible de charger des modules WebAssembly dans une application javascript et de partager les fonctionnalités permettant de profiter de la performance de WebAssembly et de la puissance et la flexibilité de JavaScript dans les mêmes applications, même si vous ne savez pas comment écrire du code WebAssembly.

WebAssembly permet aux applications complexes de fonctionner de façon optimale sur tous les navigateurs le supportant :

  • jeux vidéo 3D
  • applications peer to peer (jeux, édition collaborative, centralisée et décentralisée)
  • réalité virtuelle et réalité augmentée ( besoin d’une latence très faible)
  • applications de musique (streaming, mise en cache)
  • montage d’images et vidéos
  • reconnaissance d’images, IA
  • visualisation et simulation scientifique
  • interprètes de langues et machines virtuelles
  • bureau à distance
  • cryptage

Les développeurs peuvent utiliser WebAssembly pour accélérer les applications web.

Wasm et Emscripten

Vous n’écrivez bien sûr pas en code binaire, tout comme pour l’assembleur. C’est un outil qui compile en binaire… C’est un peu l’idée sous-jacente à LLVM/Emscripten. Rappelez-vous, on en a souvent parlé. Avec Clang, LLVM permet, par exemple, à un moteur comme Unreal Engine qui est écrit en C++ d’exporter des jeux et des applications en HTML5/WebGL. Avec WASM, c’est la même démarche: vous programmez en langage C, C++, Rust, …  et votre code, au lieu d’être compilé en Assembleur, le sera en “Web Assembleur”.

Le tableau ci-dessous représente 3 vues différentes d’un même code:

Le format binaire n’est pas conçu pour être utilisé par les humains. C’est par exemple emscripten qui va traduire le C++ en format wasm directement.

Ainsi, le format texte (WAST) est lisible par l’homme (la spécification est toujours en cours de finalisation) et permet d’écrire ou de modifier le code pour déboguer. Vous trouverez la documentation complète sur emscripten.

Pourquoi un nouveau langage intermédiaire? Pourquoi ne pas utiliser simplement LLVM, le bytecode Java, ou .NET?  Le bitcode LLVM n’a pas été retenu parce qu’il n’est pas portable. Il contient des méta-données et est conçu pour produire des binaires exécutables, non pour fonctionner directement sur tous les systèmes. Cependant on utilise Emscriptem pour compiler le langage intermédiaire de LLVM en wasm, donc on garde le bénéfice de ses outils.

Wasm veut être aux applications dans le navigateur ce que Vulkan est à OpenGL et DirectX: un code intermédiaire universel et portable avec une vitesse d’exécution proche du natif.

Où trouver ce Wasm ?

Le plus drôle, c’est que le navigateur que vous utilisez pour lire ce blog a probablement déjà la capacité de lire le Wasm.

Il est maintenant activé sur les navigateurs Firefox 52, Mozilla, Chrome 57, Edge, Opera et Webkit.

Voici un exemple de démo utilisant Wasm:

Ahah, vous l’avez reconnu… ben oui, c’est de l’Unreal Engine exporté en HTML5/WebGL. Oui, ça repose sur Wasm!

Bon, une vidéo c’est bien, mais une véritable démo c’est mieux non ? On va être fairplay, celle-ci a été développée sous Unity. Essayez-là, c’est vous le petit tank rouge, pour tirer c’est la touche “entrée”.

Bon, c’est bien tout ça, mais si tu es développeur, tu as peut-être envie de tester toi même du code Wasm, sans passer par une solution intermédiaire. Pour cela, il te faut déjà aller sur GitHub pour récupérer les outils:

  • Wabt – The Web Assembly Binary Toolkit: contient les outils principaux.
  • Waterfall: Du python et du Wasm pour faire des bots? ça vous rappelle pas mon précédent article ? 😉
  • wasm-jit-prototype: pour pouvoir utiliser wasm en dehors du navigateur. Il s’agit d’une version “standalone” de la machine virtuelle utilisée par LLVM JIT.
  • Binaryen : Script pour exécuter le code wasm en ligne de commande.
  • Wasmint: librairie pour interpréter et déboguer du code WASM dans une appli C++.

On fait un test ?

Tout d’abord, on va tester WASM via Emscripten. Pour la suite, je vais considérer que vous êtes sous Linux. Si ce n’est pas le cas et que vous êtes sous Windows 10, ce n’est pas grave, il suffit que le BASH soit activé activé.

Si vous n’êtes pas un aficionado de Linux, sachez que quand je vous dis que vous avez besoin de xxx installé, il suffit que vous lanciez sudo apt-get install xxx pour que xxx soit installé automatiquement. Sous Bash/Win10, ça fonctionne strictement de la même façon.

Pour la suite il faut installer les outils suivants: make, gcc, clang, bison, cmake, g++.

Pour Emscripten:

Cela va prendre un certain temps. Puis, lancez:

$ source ./emsdk_env.sh

Une fois installé, créez ce petit programme “hello.c” en C:

Lancez la compilation par “

$ emcc hello.c -s WASM=1 -o hello.html

Testez ensuite en laçant hello.html dans votre navigateur web:

Nous venons donc de transformer du C en quelque-chose de lisible en HTML. Bon, rien de transcendant, et pourtant. En temps normal, si on n’utilise pas “WASM=1”, emscripten ne créé que 2 fichiers: hello.html et hello.js.
Mais avec cette option, le code de hello est bien dans un WASM, les autres fichiers n’étant présents que pour permettre de lancer ce fichier binaire.

Maintenant, si vous souhaitez installer les outils spécifiques du sdk de WASM:

Sans installer, vous pouvez déjà voir plusieurs exemples à cette page. Si on prend l’exemple du factoriel, voici le fichier WAST à gauche et une partie du WASM à droite (cliquez dessus pour agrandir):

Le fichier .WAST, c’est le script à l’origine du binaire. C’est l’équivalent du fichier .ASM avant d’être assemblé en binaire. Et à droite, il s’agit du WASM, mais sous forme textuel et commenté pour mieux comprendre.  Sinon, c’est bien un fichier binaire contenant la séquence “0061736d0d0000001000160017e017e…”.

Bon, il s’agit juste d’une petite intro à WASM. Je suis loin d’être entré dans le détail, mais nous aurons l’occasion d’y revenir un peu plus tard pour détailler un peu les outils et le fonctionnement de ce dernier.L’important, c’est que vous ayez une notion de ce qu’est le langage, de sa forme WAST, WASM, de la façon dont il est “compilé” via une sorte de machine virtuelle lancée en Javascript dans le navigateur (ou sans d’ailleurs).

En attendant, je vous invite à consulter la FAQ et les spécifications du langage.

 
 
 

Ces articles pourraient aussi vous intéresser …