La compilation incrémentale et son futur

Comment les performances ne cessent de s'améliorer

Arnaud Tournier, le 27-01-2015

La compilation incrémentale et son futur

Présenté par John Stalcup

Le principe

Une compilation fonction du nombre de modifications plutot que fonction de la taille totale de l’application.

Avec de grosses équipes, la compilation classique prend de plus en plus de temps, pour finalement refaire la même chose encore et encore.

La compilation incrémentale va répondre à se besoin.

L’année dernière

Le DevMode faisait ses adieux. Un prototype de compilateur incrémental avait été présenté mais très immature et avec beaucoup d’inconnus.

  • 5 à 15x plus rapide que l’ancien SDM
  • 3 à 4x plus rapide au démarrage que le DevMode

Les choses ne se sont pas déroulées comme prévu…

Problèmes de détection de dépendances, passage à l’échelle industriel impossible.

Des décisions ont dues etre prises :

  • Passage à une compilation incrémentale monolithique. Car la compilation incrémentale “par bibliothèque” posait trop de problèmes en terme de dépendances. Donc le compilateur ramène toutes les dépendances.
  • Ensuite, un index est créé pour connaitre facilement les sources qui ne changent pas entre deux compilations.

Au final, avec cette approche, la compilation était plus rapide qu’avec la première mouture.

  • Granularité par classe et non par bibliothèque,
  • Dommage : la ligne entre build system et compilateur est de nouveau floue.

Dans le futur, un retour à la compilation par librairie pourrait être envisagée.

Comment cela fonctionne-t-il ?

En fait, cette idée est là depuis 7 ans dans GWT, avec des essais qui ont toujours échoué au final (fichiers GWTAR par exemple).

  • Module parsing : incrémental,
  • Disc resource scanning : incrémental (écoute les changements),
  • Class parse : incrémental,
  • Type index construction for générateurs : monolithique !
  • Application AST stitching
  • Regular type index
  • Sourcemap construction : NO !
  • Linking : NO !

2 à 3x de performances en plus à attendre, suite aux futurs développements.

Suite

Avant la compilation incrémentale :

La première compilation est très longue, puis à chaque recompilation, le temps descend, mais on a un effet de seuil.

Maintenant :

Les compilations successives sont presque immédiates ! La compilation initiale prend toujours beaucoup du temps. Avec la version 2.8, des changements déjà implémentés mais pas encore rendus public vont accélerer cette première compilation (2x)

Comment coder son application pour bénéficier de toutes ces améliorations

  • Eviter les générateurs les plus longs (RPC, I18N), par exemple un layer RPC non-magique (comme protobuf ou thrift par exemple) qui ne prenne pas de temps à la compilation. Par contre pour i18n, il n’y a pas encore d’alternatives viables.
  • Custom generators : faites le maximum pour avoir une sortie stable. En effet, sinon le compilateur incrémental va avoir à refaire trop de boulot. Par exemple écrivant un générateur, il est conseillé d’utiliser par exemple des Listes ordonnées plutot que des HashMap non ordonnées afin d’avoir un code généré toujours dans le même ordre.

  • GWT.debugger(); va lancer un breakpoint dans Chrome,
  • Utiliser le dernier Chrome : pas à pas dans de grosses applications, crash pour de grosses applications
  • Amélioration de SourceMapping, on pourra activer la full obfusctation ce qui donnerait un gain de 25% sur la compilation et l’exécution

A court terme

  • Finir Java 8
  • Finir JsInterop

Long terme

  • Atteindre la vitesse de compilation de Javac
  • Déplacer les générateurs vers le build system layer, pour avoir moins de ré-exécution et 50%. Historiquement les générateurs font partie de GWT. Pour l’instant les générateurs sont surveillés par le compilateur GWT pour savoir les fichiers desquels ils dépendent. Un système de build déclare les dépendances du générateur clairement et facilitent le travail du compilateur.
  • Revenir à une compilation par bibliothèque. Cela permettra entre autres de réutiliser la compilation d’une bibliothèque pour un autre projet.
  • Réduire l’explosion combinatoire des permutations (langues, navigateurs, etc…)
  • Générer du javascript dans le syle Closure : lisibilité accrue, plus facile à intégrer du javascript (on connait le format de la sortie), optimisations plus rapidement calculées, poids des fichiers js réduit. L’équipe GWT a même discuté avec Closure pour intégrer des optimisations spécifiques. Ceci concerne également le code splitting, car Closure a déjà un concept similaire, qui passe à l’échelle plus facilement et qui est plus facile à configurer.

Idées risquées

  • Chargement des classes à la façon Java (lazy, au runtime) : l’application est plus légère au démarrage et donc lancement plus rapide, empreinte mémoire plus faible. Ceci remplacera à terme le code-splitting.
  • Cache d’application automatique avec mises à jour en tâche de fond (avec les Service Workers)
  • Remplacer SDM avec un serveur de compilation “general purpose”.

Questions

GWT 3.0

  • gradle
  • démo de recette d’application Gwt bien foutues.

Peut-on éviter la construction des GwtAr pendant les compilations incrémentales ?

Gwtar est d’ores et déjà supprimé dans la branche 2.8 !

En SuperDevMode, le code splitting est désactivé et n’a donc aucun effet, bon à savoir !

Priorités de Gwt

Au tour de John Stalcup de poser des questions à l’audience :

Si nous devions sacrifier un peu de temps de compilation, dans quoi souhaiteriez vous investir ce sacrifice ? Vous avez le choix :

  • Temps de compilation,
  • Expérience développeur,
  • Taille de sortie,
  • Simplicité,
  • Quel est le bon équilibre

Dans l’ensemble il semble que l’assemblée soit d’accord pour améliorer la productivité.


Arnaud Tournier, le 27-01-2015