Benchmarking et optimisations en Java

Quid des optimisations utiles ou carrément devenues légendaires


Cette partie propose une liste non-exhaustive des optimisations côté développeur, qui sont utiles ou carrément devenues légendaires.

Taille des identifiants


Légende cyberurbaine n°1 : la taille des variables influent sur les performances.

taille variables

Le test consiste à accéder à une variable avec un nom long ou un nom court...Le résultat est des plus parlants, ça ne change rien du tout.

Conclusion : Aucune raison autre que religieuse à utiliser des abréviations compréhensibles des seuls initiés car le nom des variables est stocké dans un dictionnaire en tête du bytecode.

Gestion des exceptions

Légende cyberurbaine n°2 : la gestion des exceptions, c'est lent.

exceptions

Là encore, la différence est flagrante, n'est-ce pas ?

Conclusion : Si votre programme broute, inutile de virer la gestion des exceptions. La génération de la stacktrace est coûteuse, mais pas tellement (0,07 ms pour une pile de 50 sur un pc récent). Elle est en réalité dumpée depuis le natif  dans une structure javaesque ( le fichier source concerné est ThreadService.cpp).

Les entiers

Légende cyberurbaine n°3 : utiliser des byte, c'est mieux, ça prend moins de place et c'est plus rapide.


C'est tout sauf juste, soit archi-faux. Pour les opérations de comptage, le byte est systématiquement un mauvais choix, les CPU manipulant des entiers de 32 ou 64 bits.

Pour appuyer nos propos, la comparaison entre le bytecode pour incrémenter un int et un byte est très explicite.

Pour un int :

IINC  1: i 1

Pour un byte :

ILOAD 1: b
ICONST_1
IADD
I2B
ISTORE 1: b

Conclusion : Les int sont vos amis.

Utilisation du mot-clé final

Légende cyberurbaine n°4 : mettez final, ça va plus vite !

final

Et oui, ça n'améliore pas des masses le résultat, je vous laisse deviner qui est le coupable.

Conclusion : final est indispensable pour le design du code, mais HotSpot optimise ça dynamiquement !

Condition d'une boucle

Légende cyberurbaine n°5 : Bonnes performances et bon sens vont de paire !

Et c'est VRAI !

Premier code :

for(int i = 0; i < calculeValeurMax(); i++){
    ...
}

Cette version n'est pas géniale puisqu'on appelle calculeValeurMax() à chaque itération.

Deuxième code :

for(int i = calculeValeurMax() - 1; i >=0; i--){
    ...
}

C'est beaucoup mieux, on aurait également pu penser à stocker le résultat de l'appel à calculValeurMax() avant de démarrer la boucle.

Entrées / Sorties

Règle d'or: Pour une bonne hygiène de code, pensez au tampon.


IO

Conclusion : il faut systématiquement passer par un BufferedInputStream/BufferedOutputStream. Les NIO offrent de meilleures performances, mais au prix d'un code plus complexe.