Benchmarking et Optimisations en Java
Comment écrire un microbenchmark ?
Exemple introductif
Voici un exemple de code :
Ce
code trivial se décompose de la manière suivante :
- On enregistre la date de départ par l'appel à System.nanoTime().
- On effectue un boucle de 100 millions d'itération.
- Pour chaque valeur de i, notre compteur, nous appelons la méthode calculate, qui effectue un traitement avec comme données d'entrée la valeur de i et comme sortie le résultat d'un calcul dépendant de i.
- A la fin de la boucle, nous enregistrons la date de fin, calculons la différence avec la date de départ pour trouver le temps de traitement.
Rien de violent somme
toute, mais
nous allons voir que les mesures des temps d'exécution nous
réservent quelques surprises.
Soient 4 scénarios pour lesquels nous allons mesurer le
temps d'exécution :Mesures du temps d'exécution
- On garde le code tel quel
- On décommente L1 en désactivant les assertions ( par défaut pour la JVM )
- On décommente L1 en activant les assertions ( -enableassertions )
- On décommente uniquement L2
Maintenant, la question que personne n'attend : quelle version est la plus rapide ?
Mettons fin à ce suspense insoutenable et observons les résultats.
Après reconduction des tests 4 fois, rigoureusement dans les mêmes conditions bien sûr (option -server, etc...), les résultats sont les suivants :
- 38,601 ms
- 56,382 ms
- 38,502 ms
- 39,318 ms
Quelque peu stupéfiant non ? Comment C peut-il être plus rapide que B ???
Deux conclusions s'imposent à la lecture de ces chiffres :
- HotSpot a fait des siennes
- Il y'a une règle à vraiment respecter : "ne pas faire aveuglément confiance aux chiffres, chercher à comprendre leur provenance".
Ce qui est l'objet de la prochaine partie, consacrée à la méthode à suivre pour éviter d'obtenir ce genre de résultats plutôt aberrants à prime abord, en tenant compte des optimisations effectuées par HotSpot à notre insu ;)!