Inférence de types et minification du JavaScript

22 novembre 2011

David Mandelin, de Mozilla, a redonné à Velocity EU sa conférence Know your engines: how to make your JavaScript fast où il fait notamment part des dernières avancées en matière de moteurs JavaScript.
Il a déjà présenté cette conférence précédemment et je m’étais intéressé de près à ses slides. Mais elle est très technique et c’est vraiment en y assistant que j’ai pu en saisir tous les détails.

Typage faible

Un des principaux obstacles auxquels les moteurs JavaScript doivent faire face tient à la nature même du langage. C’est son absence de typage explicite des variables qui leur donne le plus de fil à retordre.
Rien d’étonnant que Dart, successeur du JavaScript proposé par Google, offre un typage optionnel, mais ceci est une autre histoire…
Cette flexibilité qui facilite l’utilisation de ce langage pose de sérieux problèmes aux ingénieurs chargés du développement des VM JavaScript. Pour optimiser au mieux l’exécution du code, ils ont recours à l’inférence de types (type inference en v.o.) qui consiste à déterminer et stocker le type des variables avec leurs valeurs. L’exemple pris par David est parlant :

x = y + z;

Dans ce code, l’opérateur + va se comporter différemment selon le type des variables y et z. Ce comportement étant déterminé en passant en revue les types des variables au moment de l’exécution. Je vous épargne les détails techniques, mais grâce à l’inférence de types, le comportement de cet opérateur va pouvoir être immédiatement défini. Le type des variables est déjà connu grâce au contexte précédent ce bout de code.

Constance des types

La condition de l’efficacité de l’inférence de types est la stabilité du type des variables. Si le type d’une variable change au cours d’un script cela peut forcer le moteur à dés-optimiser le code et rendre ainsi son exécution moins rapide.

Fort heureusement, les développeurs n’ont pas pour habitude de se resservir des mêmes noms de variables sous des types différents. Le problème vient plutôt des minificateurs JavaScript, en particulier Closure Compiler. Ce dernier peut réutiliser le nom d’une variable si celle-ci ne sert plus (moins de noms de variables = code plus léger + mieux gzippé). Exemple :

for(var x = 0; x < 10; x++) {...}
var str='abcdefghijklmnop'.substr(x);
return str + str;

va être minifié par Closure Compiler en :

for(var a = 0; a < 10; a++) {...}
a = 'abcdefghijklmnop'.substr(a);
return a + a;

Nous avons initialement une variable x de type number et str de type string. Le minificateur n’utilise qu’une variable a qui est de type number au début du script puis string par la suite.

Le problème de cette règle de minification est qu’elle provoque une dés-optimisation du code : le moteur JavaScript désactive l’inférence de types sur la variable a du code minifié.
Il n’existe pas de solution pour désactiver ce comportement en ligne de commande, il faut jouer directement sur l’API Java via l’option coalesceVariableNames dans com/google/javascript/jscomp/CompilationLevel.java.

Quelques précisions

Il faut quand même modérer ces propos car l’inférence de types n’est actuellement implémentée que dans Firefox à partir de la version 9. Celle-ci ne sortira en version stable qu’à la fin de l’année. V8 utilise une autre méthode d’optimisation pour palier au typage faible du JavaScript. Du coté de chez Microsoft, l’inférence de types semble être dans les cartons. Je n’ai trouvé aucune information sur les moteurs de Opera et Safari.

De plus, à ma connaissance, seul Closure Compiler est concerné, UglifyJS n’implémentera vraisemblablement pas la réutilisation de variables. Je n’ai pas vérifié YUI Compressor.

Laisser un commentaire