lundi 3 juillet 2006

BENCHMARK 64 bits C++ vs JAVA

BENCHMARK 64 bits C++ vs JAVA

Nous avons déjà réalisé un article sur les performances de C++ et de Java. Les performances se sont grandement améliorées au fil des versions de Java. Java arrivait à compétitionner avec C++ lors de certains tests.

Récemment, Sun à sortir la bêta 2 de Java 1.6, nous avons réalisé de nouveau les tests déjà effectués sur cette nouvelle version.

Détail de la machine de test

Cpu Athlon 64bits 3000+
Carte mère Asus a8n-vm
Mémoire vive 896 megs
Système d'exploitation Suse Linux 10.1
Système de fichier Reiser

Information des tests

Les mêmes tests ont été utilisés, aucun fichier source n'a été modifié. Pour de plus amples renseignements sur les sources et détails de chaque fichier, consulté l'autre article.

Protocole

Les tests en C++ ont été compilés avec g++ (GCC) 4.1.0 (SuSE Linux). La version de Java est 1.5.0_07 et 1.6.0-beta2. GCJ a aussi été utilisé.

En C++ nous avons compilé les fichiers de cette façon :

g++ -O2 -funroll-all-loops -fomit-frame-pointer -ffast-math -march=x86-64 -mfpmath=sse [test].cpp -o [test]

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é faits 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=x86-64 -mfpmath=sse  --main=[test] -o [test] [test].java

Paramètre pour chacun des programmes

Les mêmes paramètres que dans le précédent article ont été employé.

Tableau des résultats

C++ x86-64 Java 1.5 Java 1.5 -server Java 1.6 Java 1.6 -server GCJ optimisé
Ackermann 25320 20056 20020 ND ND 59705
Hash2 12080 6398 5957 4995 4920 11176
Matrix 5720 13828 13605 12169 12092 9443
Objinst 14560 5103 4969 4972 4963 28062
Strcat 660 1857 1680 1685 1607 1983
Hash 11440 45638 44915 34216 33982 33661
Methcall 7680 3273 3229 3374 3188 14062
random 3780 14640 14591 14226 14225 17684
fibo 15950 18895 17460 18076 18064 26956
heapsort 9300 11414 11215 11756 11366 10130
Nestedloop 2840 17000 16936 21303 21169 11805
Sieve 6950 14347 14340 13946 13938 10418
Total 116280 172449 168917 140718 139514 235085
ND signifie que le test a échoué

Analyse des résultats

Puisque deux tests ont échoué sous Java 1.6 le total des tests sous Java 1.6 est faussé. Nous pouvons supposer que leurs résultats pourraient être similaires à ceux obtenus sous Java 1.5.

Les résultats de Java 1.6 seraient ainsi globalement inférieurs à ceux de Java 1.5. Le test nestedloop affecte grandement Java 1.6, il semble avoir un problème, sans ce test, nous pouvons remarquer que Java 1.6 a amélioré les performances par rapport à son prédécesseur.

Les résultats sont similaires au premier article, Java s'en sort encore une fois très bien. GCJ continue d'avoir de la difficulté dans ce test.

Java 1.6 prend un avantage sur la version 1.5. Le Code C++ semble poser problème. Il est surement possible d'optimiser cela.

Le C++ se resaisie dans ce test. Il est au minimum deux fois plus vite que les versions Java. Malgré que le processeur soit plus rapide et qu'il y ai plus de mémoire, le C++ en tire beaucoup moins partie que les versions Java.

GCJ obtient des performances déplorables, presque deux fois plus lent que la version C++.Les deux versions de Java obtiennent des performances similaires.

Le code Java emploie des StringBuffer qui sont beaucoup plus rapides que des String. Depuis Java 1.5 des StringBuilder peuvent être employés, elles sont encore plus rapides que les StringBuffer. J'ai modifié le source pour voir le gain apporté par ce nouveau type de donnée et le gain a été de 200.

Nous pouvons remarquer que les performances obtenues par les versions Java sont inférieures à celle obtenue dans le précédent article. La version 64 bits de la JVM serait moins performante que la version 32 bits?

Idem que précèdent les performances sont moins bonnes.

La version C++ est dans une classe à part, elle est 3 fois plus rapide que les versions Java.


Les versions Java se sont rapprochées de la version C++ par rapport à la l'article précédèrent.


L'écart dans l'article précédemment était faible, il augment cette fois-ci.


Les résultats sont décevant pour Java 1.6, la version 1.5 est 24% plus rapide.


Aucune amélioration notable du côté de Java

Emprunte mémoire

Durant l'exécution des tests, le taux de mémoire employée et le taux d'usage du CPU ont été pris. Pour l'ensemble des tests, le CPU était utilisé à 99,9%. Concernant la mémoire, c'est plus complexe étant donné que des librairies sont partagées et ne sont pas prises en compte par les outils d'évaluation. Néanmoins à l'aide de la commande top, j'ai pu remarquer que C++ s'en sortait beaucoup mieux que ses confrères. Les versions de Java font majoritairement 50% mieux que la version de GCJ.

Facteur à prendre en considération

Performance

Les performances sont importantes, afin de bien optimiser un logiciel, l'utilisation de profiler est indispensable. Ce type d'outils permet de savoir où sont les goulots d'étranglement de l'application. Il est plus sage d'investir du temps dans une portion qui exécuté à 45% du temps par le logiciel qu'une autre qui n'est exécutée que 2% du temps.

Java permet de faire appel à du C++ à l'aide de JNI. À partir d'un programme Java, il est ainsi possible de faire appel à des fonctions en C++. Cette approche peut s'avérer intéressante lorsqu'une portion critique d'un programme Java n'atteint pas les performances souhaitées. 

L'expérience du programmeur n'est pas un facteur à négliger. Même s'il est possible de faire du code très rapide en C++, encore faut'-il être en mesure d'y parvenir sans même la stabilité du système en péril.

Temps de développement

Un autre critère qui a pris de l'importance depuis plusieurs années est le temps de développement. D'ailleurs, c'est un des points qui a favorisé le succès de Java. L'api est très riche et permet d'éviter de devoir employer de nombreuses librairies externes telles que bd, gui... qui sont difficilement portable d'un système d'exploitation à l'autre sans compté les risques d'incompatibilité. Il y a quelques années, j'ai dû porter un langage de script de Qnx vers Linux. Énormément de changement ont dû être fait, certaines API n'existaient pas sous Linux et auraient dû être écrites au complet. Cela a contribué à restreindre certaines fonctionnalités lors du portage. Idem pour une application qui fonctionnait sous Unix. Linux ne gérait pas les chaînes de caractère nulles comme le faisait Unix. Java apporte aussi la robustesse aux applications. La JVM effectue de nombreuses opérations qui sont laissées aux programmeurs en C++. C'est surement une des raisons qui fait en sorte que Java devient de plus en plus populaire dans les systèmes embarqués.

Future

Maintenant que les performances de Java sont rendues près du C++, Sun pourrait commencer à regarder d'autre approche pour améliorer Java. La JVM charge au démarrage d'un programme Java que le nécessaire, ensuite le tout est compilé et optimisé pour le processeur en cours. Cette opération est réalisée à chaque que le programme est démarré. La JVM pourrait sauvegarder le tout et réutiliser ce qui a été généré. Il y a de fortes chances que les performances soient meilleures et que moins de mémoire serait utilisée.

Conclusion

GCJ s'est améliorée depuis le premier article. Java 1.6 est encore en phase bêta, mais la majorité des résultats démontrent que les performances se sont améliorées. Les programmes compilés à l'aide de C++ obtiennent souvent les meilleurs résultats, fait à noter, les performances ne font pas tout, d'autres facteurs sont à prendre à considération lors de la phase de développement d'un logiciel. Il semble que la version 64bits de la JVM ne soit pas encore assez optimisée par rapport à la version 32 bits. Ne pas oublier que la version 1.6 est en phase bêta, ce qui influence directement les performances et stabilités.