Fermer

juin 5, 2018

Résolution de problèmes de performances sur le Web –


En JavaScript moderne, l'objectif est souvent de trouver tous les moyens d'optimiser les performances dans le navigateur. Il y a des moments où les applications Web exigent des performances élevées et attendent des navigateurs de se maintenir à jour.

Le JavaScript traditionnel a des limitations de performances en raison de la façon dont le moteur traite la langue. Un langage interprété (ou même compilé en JIT) qui est rendu en tant que partie d'une page ne peut qu'être très utile – même à partir du matériel le plus puissant.

WebAssembly est conçu pour résoudre le problème de performances. Il peut surmonter les problèmes de goulot d'étranglement que le JavaScript traditionnel n'était pas censé résoudre. Dans WebAssembly, il n'est pas nécessaire d'analyser et d'interpréter le code. WebAssembly tire pleinement parti de son format bytecode pour vous offrir des vitesses d'exécution qui correspondent aux programmes natifs.

Pensez-y d'une autre manière: imaginez le JavaScript traditionnel comme un bon outil polyvalent qui peut vous mener n'importe où. WebAssembly, en revanche, est la solution haute performance capable d'atteindre des vitesses quasi-natives. Ce sont deux outils de programmation séparés maintenant à votre disposition.

Les questions pour moi sont les suivantes: WebAssembly remplace-t-il le bon vieux JavaScript traditionnel? Sinon, vaut-il la peine d'investir dans l'apprentissage de WebAssembly?

Qu'est-ce que WebAssembly?

WebAssembly est un type de code différent qui peut être envoyé au navigateur. C'est dans bytecode format qui signifie qu'il est livré dans un langage assembleur de bas niveau au moment où il atteint le navigateur. Le bytecode n'est pas destiné à être écrit à la main, mais peut être compilé à partir de n'importe quel langage de programmation tel que C ++ ou Rust. Le navigateur peut ensuite prendre n'importe quel code WebAssembly, le charger comme code natif et obtenir de hautes performances.

Vous pouvez considérer ce WebAssembly bytecode comme un module: le navigateur peut extraire le module, le charger et l'exécuter. Chaque module WebAssembly possède des fonctions d'importation et d'exportation qui se comportent comme un objet JavaScript. Un module WebAssembly agit beaucoup comme n'importe quel autre code JavaScript, moins le fait qu'il fonctionne à des vitesses quasi-natives. Du point de vue du programmeur, vous pouvez travailler avec les modules WebAssembly de la même manière que vous travaillez avec les objets JavaScript actuels. Cela signifie que ce que vous savez déjà à propos de JavaScript et du Web est également transféré dans la programmation WebAssembly.

L'outil WebAssembly consiste souvent en un compilateur C ++. Il y a beaucoup d'outils dans le développement actuel, mais celui qui a atteint la maturité est Emscripten . Cet outil compile le code C ++ dans un module WebAssembly et construit des modules conformes aux normes qui peuvent s'exécuter n'importe où. La sortie compilée aura une extension de fichier WASM pour indiquer qu'il s'agit d'un module WebAssembly.

L'un des avantages de WebAssembly est que vous avez tous les mêmes en-têtes de cache HTTP lorsque vous récupérez des modules. De plus, vous pouvez mettre en cache des modules WASM à l'aide d'IndexedDB ou vous pouvez mettre en cache des modules à l'aide du stockage de session . La stratégie de mise en cache tourne autour de la mise en cache des requêtes d'API de récupération et évite encore une autre requête en conservant une copie locale. Puisque les modules WebAssembly sont au format bytecode, vous pouvez traiter le module comme un tableau d'octets et le stocker localement.

Maintenant que nous connaissons WebAssembly, quelles sont ses limitations?

Limitations connues

JavaScript s'exécute dans un environnement différent de tout programme C ++ typique. Par conséquent, les limitations incluent ce que les API natives peuvent faire dans un environnement de navigateur.

Les fonctions réseau doivent être asynchrones et non bloquantes. Toutes les fonctions de mise en réseau JavaScript sous-jacentes sont asynchrones dans l'API Web du navigateur. WebAssembly ne bénéficie cependant pas des opérations asynchrones liées aux E / S. Une opération d'E / S doit attendre que le réseau réponde, ce qui rend négligeables tous les gains de performances quasi-natifs.

Le code s'exécute dans un navigateur, s'exécute dans un environnement en bac à sable et n'a pas accès au système de fichiers. Vous pouvez créer un système de fichiers virtuel en mémoire à la place qui est préchargé avec des données.

La boucle principale de l'application utilise le multitâche coopératif, et chaque événement a un tour à exécuter. Un événement sur le Web provient souvent d'un clic de souris, d'un effleurement du doigt ou d'une opération de glisser-déposer. L'événement doit renvoyer le contrôle au navigateur pour que d'autres événements puissent être traités. Il est sage d'éviter de détourner la boucle des événements principaux, car cela peut se transformer en un cauchemar de débogage. Les événements DOM sont souvent liés aux mises à jour de l'interface utilisateur, qui sont coûteuses. Et cela nous amène à une autre limitation.

WebAssembly ne peut pas accéder au DOM; il s'appuie sur les fonctions JavaScript pour apporter des modifications. Actuellement, il y a une proposition pour permettre l'interopérabilité avec les objets DOM sur le web . Si vous y réfléchissez, les repeints DOM sont lents et chers. Tous les gains que l'on obtient d'une performance proche-native sont contrecarrés par le DOM. Une solution consiste à résumer le DOM en une copie locale en mémoire qui peut être réconciliée plus tard par JavaScript.

Dans WebAssembly, un bon conseil est de s'en tenir à ce qui peut fonctionner très rapidement. Utilisez l'outil pour le travail qui génère le plus de gains de performance tout en évitant les pièges. Pensez à WebAssembly comme ce système à très haut débit qui fonctionne bien de manière isolée sans aucun bloqueur.

La compatibilité du navigateur dans WebAssembly est lugubre, sauf dans les navigateurs modernes. Il n'y a pas de support dans IE. Edge 16+, cependant, prend en charge WebAssembly. Tous les grands joueurs modernes comme Firefox 52+, Safari 11+ et Chrome 57+ supportent WebAssembly. Une idée est d'avoir la détection de fonctionnalités et de faire la parité entre les modules WebAssembly et un repli sur JavaScript. De cette façon vous ne cassez pas le web et les navigateurs modernes obtiennent tous les gains de performance de WebAssembly.

Puis-je utiliser wasm? Données sur la prise en charge de la fonction wasm sur les principaux navigateurs de caniuse.com.

WebAssembly Demo

Assez parlé; temps pour une belle série de démos. Cette fois, nous allons explorer les fonctions d'exportation et d'importation dans WebAssembly. Les fonctions d'exportation et d'importation sont les caractéristiques de l'interopérabilité avec WebAssembly. Ces fonctions permettent aux programmeurs de travailler avec les modules WebAssembly comme n'importe quel autre objet JavaScript.

Une fonction d'exportation est celle que vous obtenez d'un module WebAssembly. Une fois qu'un module est chargé, vous trouverez la fonction d'exportation dans instance.exports . Pour cette démo, je vais exporter une fonction add qui calcule la somme de deux nombres que vous passez en paramètres. Le calcul s'exécutera dans un code WebAssembly quasi-natif. Dans cette démo, la fonction d'exportation sera une pure fonction JavaScript, ce qui signifie qu'elle est sans état et immuable.

Une fonction d'importation est celle que vous introduisez dans un module WebAssembly. C'est un vieil objet JavaScript simple qui a une fonction de rappel. Ensuite, le module appelle la fonction avec les paramètres de WebAssembly. J'importe un callback simple qui reçoit un paramètre de WebAssembly. Le paramètre est une constante affectée d'une valeur de 42. J'utiliserai ensuite cette valeur pour définir le DOM à partir de JavaScript:

Ajouter un résultat:

Résultat simple:

Fonction WebAssembly exportée

Tout d'abord, jetons un coup d'oeil au format texte du module WebAssembly. Ceci est une représentation textuelle du module WASM qui peut être lu par les humains. Il est conçu pour les éditeurs de texte ou tout autre outil pouvant fonctionner avec du texte brut:

 (module
  (func $ add (param $ lhs i32) (param $ rhs i32) (résultat i32)
    get_local $ lhs
    get_local $ rhs
    i32.add)
  (export "ajouter" (func $ ajouter)))

Il n'est pas trop important de comprendre chaque détail ici. C'est le format texte du module WebAssembly que vous trouvez souvent avec une extension de fichier WAT. Le i32.add effectue l'addition en utilisant du code quasi-natif. L'export "add" récupère ensuite func $ add et le met à disposition de JavaScript.

Pour charger le module WebAssembly, vous pouvez le faire:

 // URL au module WASM
const WASM_ADD_MODULE = 'https://myhost.com/add.wasm';

récupérer (WASM_ADD_MODULE)
  .then (réponse => response.arrayBuffer ())
  .then (bytes => WebAssembly.instantiate (octets))
  .then (resultat => document.getElementById ('addResult'). innerHTML =
    result.instance.exports.add (1, 5));

L'API Fetch obtient le module d'une URL et le transforme en un tableau d'octets. Ce tableau d'octets provient du response.arrayBuffer . Notez que l'inspection de la fonction exportée exports.add indique qu'elle est compilée en tant que code natif:

 function 0 () {
  [native code]
}

Un gotcha est qu'utiliser WebAssembly.instantiate est plus clément que WebAssembly.instantiateStreaming . Ce dernier dit que les modules WASM doivent avoir un type MIME de application / wasm . Vous rencontrerez ce problème lorsque vous obtiendrez [Type1945r1919] TypeError tout en travaillant avec. Si vous distribuez des modules WASM via un CDN et que vous ne pouvez pas contrôler le type MIME, utilisez WebAssembly.instantiate . WebAssembly.instantiateStreaming est plus efficace que l'ancien, mais il s'agit d'une nouvelle API Web, donc elle n'est pas encore disponible dans tous les navigateurs modernes.

Fonction WebAssembly importée

Pour les fonctions importées, commencez par ce module. format texte Imaginez faire ce calcul lié au CPU et coûteux dans WebAssembly. Tellement intense, en fait, c'est la réponse à la question ultime de la vie et de tout.

Par exemple:

 (module
  (func $ i (importer "imports" "import_func") (param i32))
  (func (export "export_func")
    i32.const 42
    appelez $ i))

Notez la constante i32.const 42 étant déclarée. Ensuite, prenez la fonction importée et appelez la fonction de rappel avec appelez $ i . L'exportation "exported_func" déclare le nom de la fonction exportée que l'on appelle de JavaScript

En JavaScript, on peut travailler avec ce module de cette façon:

 const WASM_SIMPLE_MODULE = 'https: // myhost.com/simple.wasm ';

const simpleFn = (arg) => document.getElementById ('simpleResult'). innerHTML = arg;
const importSimpleObj = {imports: {imported_func: simpleFn}};

récupérer (WASM_SIMPLE_MODULE)
  .then (réponse => response.arrayBuffer ())
  .then (bytes => WebAssembly.instantiate (bytes, importSimpleObj))
  .then (résultat => result.instance.exports.exported_func ());

Regardez importSimpleObj car il s'agit de l'objet JavaScript qui a la fonction de rappel. Le fichier exports.exported_func exécute ensuite le module WebAssembly. Une fois appelée, la fonction importée simpleFn s'exécute avec le paramètre constant

Voici une démo CodePen avec laquelle vous pouvez jouer. N'hésitez pas à inspecter chaque fonction et objet sur cet exemple de code. Cela donnera une bonne idée du code de la colle nécessaire à l'intégration avec WebAssembly

Voir le Pen WebAssembly Demo par SitePoint ( @SitePoint ) le CodePen

Conclusion

Pour répondre à mes premières questions originales, WebAssembly est un bon complément au Web. Ce n'est pas destiné à remplacer JavaScript, mais seulement améliore la technologie Web actuelle. Tout ingénieur Web à la recherche de rapidité, d'efficacité et de haute performance devrait se tourner vers WebAssembly. JavaScript fonctionne comme le code de collage qui exécute et gère le résultat de WebAssembly.

Une idée consiste à porter le code JavaScript existant qui fait beaucoup de travail lié au CPU – par exemple, une représentation virtuelle en mémoire du DOM qui ne fait que le vrai DOM. Le port WebAssembly, par exemple, peut également fournir un retour élégant aux navigateurs qui ne supportent pas encore WebAssembly.

Comme les modules WebAssembly deviennent plus répandus, les paquets npm peuvent venir avec ces modules qui sont derrière une belle abstraction JavaScript. Cela améliore à la fois l'écosystème actuel et augmente la réutilisation du code. Il peut arriver un moment où la création de vos propres modules WebAssembly ne sera pas nécessaire.

Les possibilités sont infinies avec WebAssembly. C'est un outil que vous pouvez ajouter à votre arsenal maintenant – pour résoudre beaucoup de goulots d'étranglement de performance que l'on rencontre sur le Web.




Source link