Suivez-nous

Réseau

FTLJIT : des détails par l’équipe WebKit

neilime

Publié le

 

Par

three_tier_performance.png

Nous l’évoquions il y a quelques jours, Apple et l’équipe dédiée à WebKit travaillent à la mise en place d’un nouveau moteur d’exécution JavaScript. Il faut dire que depuis les débuts du web, l’utilisation du JavaScript par les concepteurs de sites internet a largement évolué passant de simples scripts pour valider les réponses d’un formulaire à des bibliothèques entières servant de base à la construction d’applications complètes dans le navigateur à faire pâlir certaines applications natives.

Dans un message posté sur le blog de l’équipe WebKit, Filip Pizlo revient sur la genèse du nouveau compilateur JavaScript FTLJIT désormais activé par défaut dans les versions de développement rendues publiques. Il explique notamment qu’il existe principalement deux types de code JavaScript à exécuter.

  • Premier type de code : ceux qui sont courts à l’exécution et ne nécessitent pas de grosses ressources matérielles, pour eux, le plus long n’est pas l’exécution proprement dite, mais le temps de chargement du code et la mémoire utilisée pour le faire. WebKit est déjà performant sur ce point
  • En revanche, deuxième type de code, ceux qui sont plutôt courts, mais mettent du temps à être exécutés, car la tâche est répétitive et consommatrice de ressources, par exemple les filtres pour les images, les moteurs de jeux ou les codecs vidéos.

Pour ce deuxième type de code, une optimisation sur du code sans cesse répété peut conduire à un gros gain en rapidité d’exécution. C’est ce que s’est employée à faire l’équipe WebKit dans son coin, jusqu’à ce qu’ils se rendent compte qu’ils étaient en train de dupliquer des techniques d’optimisation déjà largement connues et utilisées, notamment dans l’infrastructure de compilation LLVM (qui est par ailleurs largement utilisée par Apple depuis quelques années pour aider à la compilation des applis natives utilisées sous Mac et iOS).

Dès lors, le travail de l’équipe s’est concentré sur l’unification de l’infrastructure de compilation utilisée par WebKit avec le modèle d’infrastructure de compilation proposé dans LLVM afin d’en tirer parti sans réinventer la roue. Ce travail étant désormais largement avancé, l’équipe intègre le résultat de ces avancées dans le tronc commun de développement que sont les versions publiques de WebKit.

Pour comprendre la manière dont l’équipe a amélioré son infrastructure de compilation, il faut connaître comment l’outil qui permet d’exécuter le code JavaScript envoyé avec un page HTML fonctionnait jusqu’à présent.

Quand une page web demande à Safari (et donc à WebKit et son interpréteur de code JavaScript) d’exécuter des commandes JavaScript d’un certain fichier, dans l’ancienne architecture, il y avait trois manières plus ou moins optimisées de traiter les commandes, c’est-à-dire trois moteurs d’exécution différents. Le problème étant que l’optimisation du code prend elle-même du temps, il n’est pas possible d’optimiser tous les codes JavaScript sous peine que l’ensemble optimisation+exécution sur un moteur performant prenne plus de temps que la simple exécution (sans phase d’optimisation chronophage) sur un moteur moins performant. Il faut donc déterminer pour chaque fonction JavaScript lequel des trois moteurs est le plus approprié.

Par exemple et en simplifiant, une fonction appelée peu souvent sera exécutée par le moteur le moins performant des trois disponibles. On perdrait plus de temps à optimiser le code que le temps gagné à l’exécution. En revanche, si la fonction est appelée six fois au moins, le moteur de milieu de gamme sera mobilisé pour optimiser un peu le code machine produit, le temps perdu à cette optimisation du code étant regagné lors de l’exécution proprement dite du code. De même si une fonction est appelée 66 fois ou plus, on sort le moteur haut de gamme qui va perdre du temps à optimiser le code machine, mais la fonction étant exécutée plusieurs dizaines de fois, le temps perdu pour optimiser une bonne fois pour toutes le code, sera petit à petit regagné à chacune des nombreuses exécutions.

Les trois moteurs disponibles étaient :

  • LLInt pour Low Level Interpreter qui interprète le code sans le compiler,
  • Baseline JIT pour BaseLine Just-In-Time, le moteur milieu de gamme qui compile le code pour l’exécuter plus rapidement
  • et DFG JIT pour Data Flow Graph JIT qui analyse le code avant de le compiler pour l’exécuter encore plus rapidement.

    Les performances de l’exécution du code sont montrées dans le graphique ci-dessous, hors temps passé à l’optimisation. La barre la plus longue correspond à l’exécution la plus rapide.

Avec la nouvelle solution FTLJIT, la solution d’exécution passe de trois couches (les trois moteurs plus ou moins optimisés) à quatre couches, d’où son petit nom de Fourth Tier LLVM Just-In-Time.

De trois à quatre couches, on comprend alors aisément comment ont procédé les développeurs de cette nouvelle solution : ils ont ajouté un moteur très haut de gamme au dessus des trois moteurs de basse, moyenne et haute gamme. Ainsi, quand le code JavaScript est destiné à passer dans le moteur haut de gamme DFG JIT, un nouveau branchement est ajouté pour déterminer s’il est utile de procéder à une super-optimisation encore plus chronophage.

Si c’est le cas, c’est là qu’interviennent les qualités de l’infrastructure de compilation LLVM qui permet de pousser encore plus loin les optimisations réalisables qu’elles ne l’étaient déjà sur le moteur DFG qui composait la 3e couche. Dès lors, le graphique devient le suivant :

Pour plus de détails sur la manière dont fonctionne la quatrième couche et notamment la conversion du code sous une forme SSA, plus facile à optimiser pour produire un code bas niveau performant, sur la manière de gérer le ramasse-miette ou sur la gestion des types, n’hésitez pas à aller lire la présentation de Filip Pizlo.

Introducing the WebKit FTL JIT