La prise en charge de la technologie
WebAssembly (Wasm) est apparue relativement récemment dans les navigateurs. Mais cette technologie peut bien étendre les capacités du Web, ce qui en fait une plate-forme capable de prendre en charge de telles applications qui sont généralement perçues comme des ordinateurs de bureau.
La maîtrise de WebAssembly peut être une tâche intimidante pour les développeurs Web. Cependant, le compilateur
AssemblyScript peut améliorer la situation.
L'auteur de l'article, dont nous publions la traduction aujourd'hui, propose d'abord de expliquer pourquoi WebAssembly est une technologie très prometteuse, puis de voir comment AssemblyScript peut aider à libérer le potentiel de Wasm.
Webassembly
WebAssembly peut être appelé une langue de bas niveau pour les navigateurs. Il donne aux développeurs la possibilité de créer du code qui se compile en autre chose que JavaScript. Cela permet aux programmes inclus dans les pages Web de fonctionner presque aussi rapidement que les applications natives pour diverses plates-formes. Ces programmes s'exécutent dans un environnement limité et sécurisé.
Des représentants des équipes responsables du développement de tous les principaux navigateurs (Chrome, Firefox, Safari et Edge) ont été impliqués dans la création de la norme WebAssembly. Ils se sont mis d'accord sur une
architecture système début 2017. Désormais, tous les navigateurs ci-dessus prennent en charge WebAssembly. En fait, cette technologie peut être utilisée dans
environ 87% des navigateurs.
Le code WebAssembly existe au format binaire. Cela signifie qu'un tel code est plus petit qu'un code JavaScript similaire et se charge plus rapidement. De plus, le code Wasm peut être présenté au
format texte , afin que les gens puissent le lire et le modifier.
Lorsque la norme WebAssembly est apparue pour la première fois, certains développeurs pensaient qu'elle pourrait bien remplacer JavaScript et devenir la langue principale du Web. Mais WebAssembly est mieux vu comme un nouvel outil qui s'intègre bien avec la plate-forme Web existante. C'est l'un de ses
objectifs prioritaires .
Au lieu de remplacer JavaScript là où ce langage a été utilisé pendant longtemps et avec succès, WebAssembly offre de nouvelles opportunités intéressantes pour les développeurs Web. Certes, le code Wasm n'a pas d'accès direct au DOM, donc la plupart des projets Web existants continueront à utiliser JavaScript. Ce langage, au fil des années de développement et d'optimisation, est déjà assez rapide. Et WebAssembly a ses propres
applications :
- Jeux
- Calculs scientifiques, visualisations, simulations.
- Applications CAO.
- Édition d'images et de vidéos.
Toutes ces utilisations de Wasm sont liées par ce que leurs applications respectives sont généralement considérées comme des ordinateurs de bureau. Mais du fait que WebAssembly vous permet d'atteindre un niveau de performance proche du natif, de nombreuses applications similaires peuvent désormais être implémentées à l'aide de la plateforme Web.
WebAssembly peut tirer parti des projets Web existants. Un exemple est le projet
Figma . Grâce à l'utilisation de Wasm, le temps de chargement de ce projet a été considérablement amélioré. Si un site Web utilise un code qui effectue des calculs lourds, alors, afin d'améliorer les performances de ce site Web, il est logique de remplacer uniquement ce code par un analogue de WebAssembly.
Vous voudrez peut-être essayer d'utiliser WebAssembly dans vos propres projets. Cette langue peut être apprise et écrite
immédiatement dessus . Mais, néanmoins, WebAssembly a été initialement développé comme
cible de compilation pour d'autres langues. Il a été
conçu avec un bon support pour C et C ++. Le
support expérimental de Wasm est apparu dans Go 1.11. Beaucoup d'efforts sont déployés pour écrire des applications Wasm dans
Rust .
Mais il est tout à fait possible que les développeurs Web ne veuillent pas apprendre le C, C ++, Go ou Rust uniquement pour utiliser WebAssembly. Que font-ils? La réponse à cette question peut donner AssemblyScript.
AssemblyScript
AssemblyScript est un compilateur qui convertit le code TypeScript en code WebAssembly. TypeScript est un langage développé par Microsoft. Il s'agit d'un sur-ensemble de JavaScript, avec une prise en charge améliorée des types et d'autres fonctionnalités. TypeScript est devenu un langage assez
populaire . Il convient de noter que AssemblyScript ne peut convertir en Wasm qu'un ensemble limité de constructions TypeScript. Cela signifie que même quelqu'un qui n'est pas familier avec TypeScript peut rapidement apprendre ce langage à un niveau suffisant pour écrire du code que AssemblyScript comprend.
De plus, étant donné que TypeScript est très similaire à JavaScript, nous pouvons dire que la technologie AssemblyScript permet aux développeurs Web d'intégrer facilement des modules Wasm dans leurs projets sans avoir besoin d'apprendre un tout nouveau langage.
Exemple
Écrivons notre premier module AssemblyScript. Tout le code dont nous allons discuter maintenant se trouve sur
GitHub . Pour
prendre en charge WebAssembly, nous avons besoin d'au moins
Node.js 8.
Créez un nouveau répertoire, initialisez le projet npm et installez AssemblyScript:
mkdir assemblyscript-demo cd assemblyscript-demo npm init npm install --save-dev github:AssemblyScript/assemblyscript
Notez que AssemblyScript doit être installé directement à partir
du référentiel GitHub du projet. AssemblyScript n'a pas encore été publié dans npm, car les développeurs
ne le considèrent pas encore prêt pour une utilisation généralisée.
Nous allons créer des fichiers auxiliaires en utilisant la commande
asinit
incluse dans AssemblyScript:
npx asinit .
La section
scripts
de notre
package.json
devrait maintenant prendre la forme suivante:
{ "scripts": { "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug", "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize", "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized" } }
Le fichier
index.js
situé dans le dossier racine du projet ressemblera à ceci:
const fs = require("fs"); const compiled = new WebAssembly.Module(fs.readFileSync(__dirname + "/build/optimized.wasm")); const imports = { env: { abort(_msg, _file, line, column) { console.error("abort called at index.ts:" + line + ":" + column); } } }; Object.defineProperty(module, "exports", { get: () => new WebAssembly.Instance(compiled, imports).exports });
Cela vous permet d'inclure des modules WebAssembly dans votre code à l'aide de la commande
require . C'est-à-dire de la même manière que les modules JavaScript normaux sont connectés.
Le dossier d'
assembly
contient le fichier
index.ts
. Il a du code source écrit conformément aux règles AssemblyScript. Le code passe-partout généré automatiquement est une fonction simple pour ajouter deux nombres:
export function add(a: i32, b: i32): i32 { return a + b; }
Peut-être vous attendiez-vous à ce que la signature d'une fonction similaire ressemble à
add(a: number, b: number): number
. Il semblerait donc qu'il soit écrit en TypeScript ordinaire. Mais ici, au lieu du type
number
, le type
i32
est
i32
. Cela se produit car le code AssemblyScript utilise des
types WebAssembly spécifiques pour les entiers et les nombres à virgule flottante, plutôt que le type de
nombre générique de TypeScript.
Assemblons le projet:
npm run asbuild
Les fichiers suivants doivent apparaître dans le dossier de
build
:
optimized.wasm optimized.wasm.map optimized.wat untouched.wasm untouched.wasm.map untouched.wat
Il existe des versions optimisées et régulières de l'assemblage. Chaque version de l'assembly nous donne un fichier .wasm binaire, une
carte map .wasm.map et une représentation textuelle du code binaire dans un fichier .wat. La présentation de test du code Wasm est destinée au programmeur, mais nous ne regarderons même pas ce fichier. En fait, l'une des raisons de l'utilisation de AssemblyScript est qu'il élimine le besoin de travailler avec du code Wasm.
Maintenant, exécutons Node.js en mode REPL et assurez-vous que le module Wasm compilé peut être utilisé de la même manière que n'importe quel module JS normal:
$ node Welcome to Node.js v12.10.0. Type ".help" for more information. > const add = require('./index').add; undefined > add(3, 5) 8
En général, c'est tout ce qui est nécessaire pour utiliser la technologie WebAssembly dans Node.js.
Équiper un projet avec un script d'observateur
Afin de reconstruire automatiquement le module pendant le développement lorsque vous y apportez des modifications, je recommande d'utiliser le package
onchange . Le fait est que AssemblyScript
n'a pas encore son propre système de surveillance des modifications de fichiers. Installez le package onchange:
npm install --save-dev onchange
Ajoutez le
asbuild:watch
à
package.json
.
L'indicateur -i
est inclus dans la commande afin que le processus de génération démarre une fois lorsque le script est appelé, avant qu'aucun événement ne se produise.
{ "scripts": { "asbuild:untouched": "asc assembly/index.ts -b build/untouched.wasm -t build/untouched.wat --sourceMap --validate --debug", "asbuild:optimized": "asc assembly/index.ts -b build/optimized.wasm -t build/optimized.wat --sourceMap --validate --optimize", "asbuild": "npm run asbuild:untouched && npm run asbuild:optimized", "asbuild:watch": "onchange -i 'assembly/**/*' -- npm run asbuild" } }
Maintenant, au lieu d'exécuter constamment la commande
asbuild
, exécutez simplement
asbuild:watch
une fois.
Performances
Nous allons écrire un test simple qui évaluera le niveau de performance du code Wasm. Le principal objectif de WebAssembly est de résoudre des tâches qui utilisent intensivement le processeur. Par exemple, ce sont des calculs «lourds». Créons une fonction qui découvre si un certain nombre est premier.
L'implémentation JS de base d'une fonction similaire est illustrée ci-dessous. Il est arrangé très simplement, il vérifie le nombre par force brute, mais pour nos fins il convient, car il effectue de grandes quantités de calculs.
function isPrime(x) { if (x < 2) { return false; } for (let i = 2; i < x; i++) { if (x % i === 0) { return false; } } return true; }
Une fonction similaire, écrite pour le compilateur AssemblyScript, a presque la même apparence. La principale différence est la présence d'annotations de type dans le code:
function isPrime(x: u32): bool { if (x < 2) { return false; } for (let i: u32 = 2; i < x; i++) { if (x % i === 0) { return false; } } return true; }
Pour analyser les performances du code, nous utiliserons le package
Benchmark.js . Installez-le:
npm install --save-dev benchmark
Créez le fichier
benchmark.js
:
const Benchmark = require('benchmark'); const assemblyScriptIsPrime = require('./index').isPrime; function isPrime(x) { for (let i = 2; i < x; i++) { if (x % i === 0) { return false; } } return true; } const suite = new Benchmark.Suite; const startNumber = 2; const stopNumber = 10000; suite.add('AssemblyScript isPrime', function () { for (let i = startNumber; i < stopNumber; i++) { assemblyScriptIsPrime(i); } }).add('JavaScript isPrime', function () { for (let i = startNumber; i < stopNumber; i++) { isPrime(i); } }).on('cycle', function (event) { console.log(String(event.target)); }).on('complete', function () { const fastest = this.filter('fastest'); const slowest = this.filter('slowest'); const difference = (fastest.map('hz') - slowest.map('hz')) / slowest.map('hz') * 100; console.log(`${fastest.map('name')} is ~${difference.toFixed(1)}% faster.`); }).run();
Voici ce que j'ai réussi à obtenir après avoir exécuté la commande de
node benchmark
sur mon ordinateur:
AssemblyScript isPrime x 74.00 ops/sec ±0.43% (76 runs sampled) JavaScript isPrime x 61.56 ops/sec ±0.30% (64 runs sampled) AssemblyScript isPrime is ~20.2% faster.
Comme vous pouvez le voir, l'implémentation AssemblyScript de l'algorithme était 20% plus rapide que l'implémentation JS. Cependant, notez que ce test est une
microbenchmark . Ne vous fiez pas trop à ses résultats.
Afin de trouver des résultats plus fiables de la recherche de performances des projets AssemblyScript - je recommande de jeter un œil à
ceci et à
ces benchmarks.
Utilisation d'un module Wasm sur une page Web
Utilisons notre module Wasm sur une page Web. Nous commençons par créer un fichier
index.html
avec le contenu suivant:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>AssemblyScript isPrime demo</title> </head> <body> <form id="prime-checker"> <label for="number">Enter a number to check if it is prime:</label> <input name="number" type="number" /> <button type="submit">Submit</button> </form> <p id="result"></p> <script src="demo.js"></script> </body> </html>
Créez maintenant le fichier
demo.js
dont le code est illustré ci-dessous. Il existe de
nombreuses façons de charger des modules WebAssembly. Le plus efficace est de les compiler et de les initialiser en mode streaming à l'aide de la fonction
WebAssembly.instantiateStreaming . Veuillez noter que nous devrons redéfinir la fonction d'
abandon , qui est appelée si une
instruction n'est pas exécutée.
(async () => { const importObject = { env: { abort(_msg, _file, line, column) { console.error("abort called at index.ts:" + line + ":" + column); } } }; const module = await WebAssembly.instantiateStreaming( fetch("build/optimized.wasm"), importObject ); const isPrime = module.instance.exports.isPrime; const result = document.querySelector("#result"); document.querySelector("#prime-checker").addEventListener("submit", event => { event.preventDefault(); result.innerText = ""; const number = event.target.elements.number.value; result.innerText = `${number} is ${isPrime(number) ? '' : 'not '}prime.`; }); })();
Ensuite, installez le package de
serveur statique . Nous avons besoin d'un serveur car pour pouvoir utiliser la fonction
WebAssembly.instantiateStreaming
, le module doit être entretenu à l'aide du type MIME
application/wasm
.
npm install --save-dev static-server
Ajoutez le script approprié à
package.json
:
{ "scripts": { "serve-demo": "static-server" } }
npm run serve-demo
maintenant
npm run serve-demo
et ouvrez l'URL de l'hôte local dans le navigateur. Si vous entrez un certain nombre dans le formulaire, vous pouvez savoir s'il est simple ou non. Maintenant, en développant AssemblyScript, nous avons parcouru un long chemin entre l'écriture de code et son utilisation dans Node.js et sur une page Web.
Résumé
WebAssembly, et donc AssemblyScript, n'est pas en mesure d'accélérer un site comme par magie. Mais Wasm n'a jamais été chargé d'accélérer absolument tout. Cette technologie est remarquable en ce qu'elle ouvre la voie au Web pour de nouveaux types d'applications.
On peut dire quelque chose de similaire à propos d'AssemblyScript. Cette technologie simplifie l'accès à WebAssembly pour un grand nombre de développeurs. Il permet, lors de la création de code dans un langage proche de JavaScript, d'utiliser les capacités de WebAssembly pour résoudre des problèmes de calcul complexes.
Chers lecteurs! Comment évaluez-vous les perspectives d'utilisation d'AssemblyScript dans vos projets?
