Java : benchmark de la JRE
August 5, 2008
On voit souvent des débats concernant la vitesse de Java par rapport au C++. Les développeurs Java disent que leur langage est plus rapide (sur certains points c’est vrai, mais il n’y en a pas beaucoup) et les développeurs C++ ont souvent des préjugés (très souvent dûs au passé de Java qui n’est pas des plus glorieux).
J’ai donc décidé de comparer, non pas Java avec C++, mais l’évolution de la JRE depuis la version 1.1 à la future 1.7.
Réalisation des benchmarks
J’ai réalisé ces benchs avec différents algorithmes et chargements agressifs de la JVM :
- Copie de tableau de 10Mo par la méthode : System.arrayCopy.
- Copie de tableau de 10Mo par boucle.
- Suite de Fibonacci.
- Fonction d’Ackermann.
- Exécution de boucles imbriquées.
- Concaténation de String dans un StringBuffer.
- Copie de matrice.
- Appels intensifs de méthodes.
- Instanciations intensives d’objets.
- Calculs mathématiques.
Les sources de mes benchs sont disponibles : [sources benchmarks de la JRE][1].
Voici la configuration de la machine ayant servie à exécuter les benchs : Intel Core 2 duo cadencé à 3.00 Ghz, 2Go de mémoire, Windows XP Professional SP2.
Tous les résultats sont en millisecondes, sauf pour la taille de la JRE qui elle est en kilooctets.
Taille de la JRE
La JRE n’a cessé de grossir au fur et à mesure des versions avec sans cesse de très nombreuses améliorations de l’API (le point fort de Java).
Copie de tableau : System.arrayCopy
Dans ce bench je crée un tableau (la création du tableau n’est pas inclue dans les résultats) de 10Mo et je recopie ce même tableau dans un autre tableau grâce à la méthode System.arrayCopy.
Copie de tableau : boucle
Ce bench est très similaire au précédent sauf que le tableau est copié byte à byte par une boucle (ce qui est déjà beaucoup plus long).
Suite de Fibonacci
La suite de Fibonacci est un algorithme récursif qui est souvent utilisé pour effectuer des benchmarks. Cela dit les résultats sont très parlant quand à l’évolution de la récursivité dans la JVM.
Fonction d’Ackermann
La fonction d’Ackermann est aussi un algorithme récursif un peu plus agressif que la suite de Fibonacci.
Boucles imbriquées
Ce test va tout simplement exécuter plusieurs boucles imbriquées et utiliser la valeur des index des boucles.
Comme souvent les premières versions de la JRE (version 1.1 et 1.2) qui étaient très légères sont beaucoup plus performantes que les versions récentes, sauf pour la 1.6 et la future 1.7 dont les performances ont été grandement améliorées.
Concaténation StringBuffer
Dans ce bench je crée un StringBuffer *auquel je concaténe la chaîne “azertyuiopqsdfghjklmwxcvbn0123456789*” un million de fois.
Dans ce bench on voit que la gestion des String a été très largement améliorée depuis la première version de la JRE (presque deux fois plus rapide).
Copie de matrice
La multiplication de matrices permet de vérifier la vitesse de calcul de la JVM. Je teste ici 10000 multiplications de deux matrices 30×30.
Appels de méthodes
Ce bench instancie deux objets très simples (objets à deux états) et appel leurs méthodes 100 millions de fois chacuns. L’instanciation des objets n’est pas inclue dans les résultats.
Instanciation d’objets
Ce test met en évidence que Java est un langage Objet. Depuis la version 1.1 jusqu’à la version actuelle le temps d’instanciation de 100 millions d’objets a été divisé par 16 en passant de 25 313ms a 1547ms.
Calculs mathématiques
Ce test effectue 10 millions d’opérations mathématiques (modulo, division, …) en utilisant le résultat précédent comme paramètre de la fonction.
Conclusion
Si on agrège tous ces résultats pour obtenir une approximation de la vitesse d’exécution de la JRE (attention ce résultat est à relativiser, je n’ai pas effectué tous les benchmarks possibles), on observe que ce qui joue le plus grand rôle est l’instanciation des objets, or Java se veut être LE langage objet du moment.
Si vous avez des benchs a me soumettre je les ajouterais à ce billet.
[1]: http://www.mkhelif.fr/uploads/2008/08/benchmark.jar "Benchmark"