BENCHMARK C++ vs JAVA...
Pourquoi un autre benchmark
En informatique, les comparaisons sont légion. Qui ne connait pas les
guerres Intel vs Amd, Nvidia vs ATI, Windows vs Linux. Les langages de
programmation n'y échappent pas. On appose malheureusement souvent qu'un
langage est lent après avoir fait quelques tests très ciblés, ce qui
n'est souvent pas très représentatif d'un logiciel courant. Le but de ce
test est de comparer le code généré par la JVM de Sun, gcc et gcj. Le
code sera généré en version optimisée et non optimisé afin de voir ce
qu'il peuvent apporter au niveau des performances.
Nous allons utiliser
deux versions de Java dont la toute dernière, Java 1.5.
Il n'est pas rare de lire ou d'entendre que Java est lent, que le C++
c'est meilleur... Souvent ce faux préjugé provient d'une application
Java GUI qu'on a vue fonctionner. La réactivité n'était pas au
rendez-vous... Depuis, de grands changements sont survenus.
Le JIT est apparu. JIT signifie just in time compiler. Le JIT prend le
fichier *.class généré par la commande Java et compile en natif pour la
plateforme utilisée. Cette astuce permet d'avoir de bien meilleures
performances. Des outils de développement tel que Intellij, Sun Java
creator ou JBuilder sont fait en Java et n'ont pu grand-chose à envier
au autre application graphique. Il y a même une version de Quake 2 qui
est faite en Java. Il ne faut pas oublier que la vitesse ce n'est pas
tout, la facilité de développement est primordiale de nos jours.
Détail de la machine de test
Cpu |
Athlon xp 1800+ |
Carte mère |
ECS K7VMM |
Mémoire vive |
512 meg |
Système d'exploitation |
Suse Linux 9.1 |
Système de fichier |
Ext3 |
Information des tests
Les tests réalisés ont été faits à partir des sources disponibles à
JavaBench
Ces tests comparativement à d'autres sur le net ont l'avantage de
toucher à diverses fonctionnalités du langage. Ils vont beaucoup plus
loin que ceux qu'on a pu retrouver chez
osnews.
Leurs tests ne faisaient qu'effectuer des opérations mathématiques sur
divers type de donnée, ce qui est très peu pour déterminer les
performances d'un langage.
Ackermann
Ackermann est une fonction mathématique. Elle croît plus rapidement
qu'une fonction exponentielle. Son implémentation est récursive. Les
appels de fonctions sont donc testés.
Fibo
Fibonacci est la fonction très connue en mathématique : F(i)=F(i-1)+F(i-2)...
Hash et Has2
Ces deux fonctions de hachage
Heapsort
Heapsort est le trie qui a une complexité de O (n log(n). Il est donc efficace.
Matrix
Ce test effectue des calculs de matrice.
Methcall
Ce test effectue de nombreux appels de méthode à des classes.
Nestedloop
Ce test est constitué de diverse boucle imbriquée.
Objinst
Ce test instancie de nombreux objets.
Random
Ce test génère des nombres aléatoires
Sieve
Enlève les multiples dans un tableau.
Strcat
Concatène des chaînes de caractère
Wc
Les mots sont comptés.
Protocole
Les tests en C++ ont été compilés avec g++ (GCC) 3.3.3 (SuSE Linux).
La version de Java java version 1.4.2_03 et java 1.5. GCJ a aussi été utilisé.
En g++, nous avons compilé les fichiers comme:
g++ [test].cpp -O2 -march=i386 -o [test]-386
g++ [test].cpp -O2 -march=i686 -o [test]-686
g++ -O2 -funroll-all-loops -fomit-frame-pointer -ffast-math -march=i686 -mfpmath=sse [test].cpp -o [test]
En utilisant O3 les résultats étaient souvent inférieurs. L'emploi de
l'architecture Athlon au lieu de i686 n'a pas d'impact significatif sur
les résultats.
En java, nous avons compilé les fichiers de cette façon :
javac -O [test].java
Les programmes java ont été exécuté en version cliente et serveur.
java [test]
java -server [test]
Des tests ont été fait afin de voir les performances des programmes Java en natif à l'aide du compilateur gnu.
gcj -O2 -march=i686 --main=[test] -o [test] [test].java
gcj -O2 -fomit-frame-pointer -ffast-math -march=i686 -mfpmath=sse --main=[test] -o [test] [test].java
Paramètre pour chacun des programmes
Ackermann |
13 |
Hash2 |
3000 |
Matrix |
100000 |
Objinst |
100000000 |
Strcat |
10000000 |
Hash |
3000000 |
Methcall |
1000000000 |
random |
300000000 |
fibo |
45 |
heapsort |
10000000 |
Nestedloop |
45 |
Sieve |
100000 |
Prenez note que le programme wc doit être exécuté de cette façon: wc
< [nom_fichier]. Les crochets ne doivent pas être mis. Le fichier
utilisé contenant 5 millions de lignes.
Tableau des résultats
|
C++ |
C++ i686 |
C++ i686 optimisé |
Java |
Java -server |
Java 1.5 |
Java 1.5 -server |
GCJ |
GCJ optimisé |
Ackermann |
138640 |
99280 |
78140 |
n/d |
24586 |
n/d |
24551 |
269725 |
258282 |
Hash2 |
16770 |
17410 |
15980 |
*24634 |
*29840 |
17811 |
15825 |
29798 |
28967 |
Matrix |
17950 |
14010 |
8320 |
32228 |
24459 |
32228 |
24459 |
40102 |
30471 |
Objinst |
23220 |
23170 |
22600 |
15888 |
15854 |
17718 |
17122 |
45400 |
43617 |
Strcat |
1620 |
1140 |
1600 |
*3511 |
*4235 |
3504 |
3199 |
4708 |
5425 |
Hash |
16010 |
15380 |
15470 |
32300 |
30839 |
32126 |
28182 |
43297 |
41307 |
Methcall |
22350 |
14260 |
11880 |
21909 |
2836 |
21148 |
2962 |
28734 |
22605 |
random |
13330 |
6000 |
4870 |
40261 |
24129 |
44447 |
29137 |
30494 |
24567 |
fibo |
35150 |
28260 |
22950 |
24617 |
18544 |
31874 |
18957 |
70203 |
61609 |
heapsort |
31020 |
30170 |
30270 |
*32842 |
*30512 |
32474 |
32302 |
34865 |
34032 |
Nestedloop |
12380 |
11740 |
2610 |
29388 |
29813 |
35959 |
23834 |
13631 |
15814 |
Sieve |
13370 |
11740 |
12430 |
14736 |
13557 |
16898 |
15000 |
10413 |
10221 |
Wc |
1220 |
1250 |
1260 |
1260 |
1110 |
1685 |
1238 |
3714 |
3627 |
Total |
343030 |
273810 |
228380 |
273574 |
250314 |
287872 |
236768 |
625084 |
579544 |
Une * affiché à côté des tests signifit que le paramètre -Xmx256M ou -Xmx512M a été ajouté à l'exécution.
N/D signifie que le test a échoué
Analyse des résultats
Java réussit assez bien pour un langage qu'on dit souvent lent. Ses
résultats sont souvent similaires à ceux obtenus à ceux en C++. Les
diverses optimisations que Sun a apportées à sa JVM depuis ses débuts
sont très bonnes. Qui aurait pu prévoir de tel résultat il y a 5 ans?
Même si Java ne gagne pas tous les tests, il reçoit une note globale
très satisfaisante. Le résultat final n'est pas vraiment important.
L'essentiel est de remarquer que les performances de Java se sont
grandement améliorées. Les performances de Java actuellement sont
parfois supérieures à celle de C++ et d'autre fois inférieure. Sans les
problèmes de récursion éprouvé par gcc et gcj, leurs résultats globaux
seraient encore meilleurs. Le test random et matrix a été pénalisants
pour le java.
Gcc semble avoir des problèmes avec la récursion. J'ai été grandement
surpris de la faiblesse de leurs résultats. J'ai exécuté les tests à
quelques reprises. Les deux tests Java (exécuté sans le paramètre
serveur) ont échoué le test, ils n'ont pu le finir. La version i686 du
programme est vraiment beaucoup plus rapide que la version sans
optimisation. Pas loin de 40 secondes les sépares.
Les versions du logiciel en Java 1.5 obtiennent de bien meilleurs
résultats que la précédente version. Un Iterator est utilisé, Sun les
aurait semble t'il améliorer. Leurs résultats sont semblables à ceux
obtenus en C++.
Les versions C++ sont bon premier. Les exécutions serveur de Java
gagnent plus de 10 secondes sur la version 10. La façon de calculer
ligne, colonne pénaliserait'elle Java? La version optimisée de GCJ
permet d'économiser plus de 10 secondes sur celle moins optimisée. GCJ
fait pâle figure avec ses 40 secondes. Étant compilée en natif, sa
performance devrait être beaucoup plus près du C++.
Java sort grand gagnant de ce test. Les versions serveur obtiennent
les premières places. C++ n'est pas loin dernière. GCJ obtient encore un
résultat déplorable.
Les versions C++ gagnent, les autres sont 2,5 à 4 fois plus lent.
C++ est encore le plus rapide dans ce test.
Les résultats des versions serveur de Java sont vraiment incroyable.
Les autres sont loin derrière. La jvm doit faire une optimisation
vraiment spéciale pour arriver à de tels résultats.
La compilation i686 permet d'être deux fois plus rapides que la
version i386 ce qui n'est pas négligeable. Java ne réussit pas trop mal
en mode serveur. La version client n'obtient pas d'aussi bon résultat.
GCJ est entre les deux.
Tel que mentionné plutôt, étant donnée la récursion utilisée, on
pouvait déjà prévoir que Java obtiendrait de bons résultats.
L'activation du mode serveur fait gagner quelques secondes sur le mode
client. Encore une fois, l'optimisation i686 gruge quelques secondes à
son confrère. Les résultats obtenus avec GCJ sont catastrophiques.
Les résultats sont tous similaires, une version récursive aurait peut-être permis à Java de se démarquer.
GCJ obtient enfin de bons résultats, suivent ensuite les versions
C++. Java 1.5 en mode serveur réussi à surpasser le mode client et Java
1.4.
GCJ gagne, il réussi à battre les versions du programme en C++.
Mis à part GCJ, les résultats sont similaire.
Nous avons vu que Java 1.5 n'apportait pas de grande révolution au
niveau des performances. Le mode serveur en Java 1.5 demeure comme dans
sa version précédente, plus rapide que le mode client. GCJ semble avoir
quelques lacunes, il est compilé en natif, mais obtiens souvent des
résultats inférieurs à Java. Les possibilités d'optimisation qu'offre le
compilateur gnu peuvent s'avérer intéressantes dans certains cas.
Les fichiers sources ainsi que les fichiers nécessaires pour compiler et exécuter les programmes sont disponibles
ici.