Salut,
Je trifouillais un peu sur du Scala récemment, et j'ai été surpris de la relative lenteur de la bête, malgré ce qu'on en dit. Du coup, j'ai voulu faire des tests, pas du tout significatifs, pour comparer ça à C++ et Java, et les résultats m'ont quelque peu... surpris, ne serait-ce qu'entre C++ et Java.
Voyez par vous-même :
Code en java : http://pastebin.com/9XGJBSmq
Code en C++ : http://pastebin.com/DHvfw9tG
Code en Scala : http://pastebin.com/WiyYrRDP
Donc, un petit test basique, une boucle d'un milliard d'itérations (petite différence, on a une assignation pour le code Java, parce que je connais pas particulièrement le Java et que le compilo me faisait une erreur sans)
Et là, exécution :
$ time java Test
java Test 0,22s user 0,03s system 104% cpu 0,238 total
$ time ./test
./test 9,34s user 0,01s system 99% cpu 9,400 total
$ time scala TestScala
scala TestScala 11,46s user 0,16s system 101% cpu 11,459 total
Comme vous le voyez, le Java bronche pas alors que le cpp et le scala sont bien plus longs.
Du coup, j'ai un peu de mal à comprendre d'où ça vient. De ce que j'ai pu voir, j'ai ~0.2s de chargement de la JVM quand j'exécute Java, et ~0.9s de chargement de cette même JVM+libs Scala quand j'exécute du Scala. Mais, même sans ces délais, la différence est énorme.
C'est sans doute tout bête, mais quelqu'un sait pourquoi on a une telle différence, même déjà juste entre le java et le C++ ? Et, éventuellement, avec le Scala ?
(pour le Scala, on passe à 2,2s d'exécution pour le même code si je passe -optimize au compilo, mais la compilation dure une cinquantaine de secondes au lieu des ~15 habituelles).
Bon, ça reste toujours plus rapide que de l'interprété (le code ruby ( http://pastebin.com/064rF2xu ) mets 3min
) mais ça m'étonne quand même.
Comment ça se fait que le Java bronche pas, que la boucle fasse 10 ou 1000000000 itérations, par rapport au CPP ? ![]()
Déjà tes différents code ne font pas la même chose, difficile de se baser la-dessus pour comparer les perfs de différents langages.
Ensuite entre Java et C++ il y a des optimisations qui peuvent entrer en compte : par défaut le code Java est fortement optimisé alors qu'il faut généralement le spécifier au compilateur lorsqu'on veut optimiser du C++ (ajout du flag -O2 par exemple avec g++). Ainsi, ça ne m'étonnerait pas que Java n'évalue même pas l'expression z = x * x : la valeur affectée n'est jamais lue et l'opération x * x n'a pas d'effet de bord, l'instruction n'a donc aucun intérêt.
Concernant Scala, je ne sais pas trop. Utiliser des boucles for n'est pas forcément une bonne idée. Je ne sais pas comment va être interprété ton code, mais c'est possible que ça définisse une collection de 1 000 000 000 d'éléments sur laquelle tu itères. Et même si les éléments de cette collection sont générés au fur et à mesure des besoins, peut-être que les éléments générés restent stockés en mémoire (ce qui impose de réallouer régulièrement de l'espace).
" Déjà tes différents code ne font pas la même chose, difficile de se baser la-dessus pour comparer les perfs de différents langages. "
Ah ? Mise à part le Java qui fait une assignation, c'est la même chose, non ?
"Ainsi, ça ne m'étonnerait pas que Java n'évalue même pas l'expression z = x * x : la valeur affectée n'est jamais lue et l'opération x * x n'a pas d'effet de bord, l'instruction n'a donc aucun intérêt. "
Ah d'accord, effectivement ça semble logique et ça expliquerait pourquoi le temps ne varie pas pour i de 10 à 1.10e8.
Pour le Scala, c'est plausible, mais c'est vraiment toutes les opérations qui semblent être affreusement longues. Un simple "Hello World!" statique mets 0.9s ; un simple "Hello " + args(0) mets 1.7 secondes, et ce sont pourtant les exemples d'Odersky dans son livre ![]()
(Merci pour ta réponse en tout cas, ça m'éclaircit pas mal de points
)
Tu peux juste faire le test sur ta machine et me dire combien de temps ça met pour le C++ avec g++ avec l'option -O2 s'il te plait ?
J'aimerais bien savoir ![]()
Voir même -O3 en fait ![]()
Mêmes résultats sans, avec -O2 et avec -O3.
Sinon, étrange. Je viens de tester le scala sur ma machine fixe sous debian Stable, moins puissante que mon laptop (sous debian Testing), et là, Scala est bien plus rapide : 0.2s pour un Hello World, pas de différences avec un Hello World statique ou un print des arguments, et 'seulement' 7 secondes pour le test des itérations, soit presque 2 fois plus rapide
J'ai pensé que c'était du à la version d'openjdk et icedTea, mais j'ai les mêmes résultats sur mon laptop en repassant en openjdk-6 et icedTea-1.12 (contrairement à openjdk-7 et icedTea-2.24) ![]()
Caletlog, il n'y a aucune raison qu'un code java soit plus rapide qu'un code C++ equivalent. Les seuls exemple que j'ai pus voir etait liee a un programmeur C++ qui ne comprenait pas ce qu'ils faisait (en particulier, comment gerer de la memoire).
Des que tu fais des tests de performance, il faut comparer des choses en utilisant pleinement ses outils, et donc souvent c'est une compilation en -O2 ou -O3 avec declaration de l'architecture qui va bien (dans gcc -march=native ).
Si tu as des questions de perf, n'hesite pas a les poser, c'est un peu mon boulot
En passant une colle: ecrit le code qui fait le plus d'operation floatante par secondes. Je me contre fout de ce que les operations font ou de la logique derriere ces operation. Met juste le plus d'operation flotante que tu peux par secondes.
Poste les caracteristiques de ton (tes?) processeur(s?) avec tes resultats.
godrik > Je veux bien ; c'est ce que je pensais aussi. Mais du coup je comprends pas mes résultats ^^"
On est d'accord, ces deux programmes font bien la même chose, maintenant ?
Alors pourquoi de tels résultats ? (fenêtre du bas)
Pour ta dernière question, je suis pas sûr de comprendre ce que tu veux montrer en fait... Un truc comme ça, tu veux dire ? http://stackoverflow.com/questions/8389648/how-do-i-achieve-the-theoretical-maximum-of-4-flops-per-cycle
@Aldebran : " [...] par défaut le code Java est fortement optimisé [...]"
Non, même plutôt l'inverse en fait. Le compilateur standard (javac) de Java n'optimise strictement rien, ou extrêmement peu. Par contre, la VM, avec le compilateur Just in Time (JIT) fait des optimisations bytecode assez agressives pour les passages les plus utilisés.
Pour être fixé, il suffit de regarder le bytecode Java généré par la compilation. Il y aura toutes les instructions, mêmes les prévisions standard pouvant être évitées statiquement.
Celles-ci seront par contre simplifiées/optimisées au runtime après quelques passages.
hotspot 1.8, g++ 4.9, amd64
g++ -O2: 0m0.963s
g++ -O3: 0m1.220s (utilisation abusive de SSE)
hotspot: 0m1.010s
hotspot génère beaucoup plus d'instructions que g++ (O2)
https://gist.github.com/aanonymous/c9165bbd566dd9569919
Rien à voir mais ça m'intrigue, c'est quoi ta police ? ![]()
Perso, je vois la meme chose que elite a peu de chose pres.
Caletlog, ce que j'essaye de te dire c'est que si tu t'interesse a la performance, il faut regarder l'execution telle qu'elle se passe dans ton processeur. Et la j'ai l'impression que tu te poses les mauvaises questions.
La question est:
-quelles sont les instructions assembleur qu'il faut emettre?
-pourquoi java emet ces instructions assembleur la?
-pourquoi gcc emet ces instructions assembleur la?
-comment modifier le code pour que le compilateur prenne la bonne decision?
Quand tu va commencer a regarder ces choses, tu pourrais bien trouver des strategie d'execution differente. Par exemple, ce qui est complique dans ton probleme, c'est que tu travailles sur des long. Et que les long c'est la mort. En particulier parceque les vecteur de long n'ont pas l'air de bien se multiplier. Mais si tu note que tes i sont en fait 32 bit, tu dois pouvoir utiliser un truc comme "pmuldq" pour faire ce que tu veux un peu plus vite.
godrik > ah okay, je vois 'de loin' le problème.
Ça m'a l'air franchement intéressant, mais je t'avoue que je suis loin d'avoir le niveau pour comprendre ça, je pense. J'ai que de très vagues notions d'assembleur et d'architecture alors je bloque déjà sur vos posts, avec elite
LEpigeon-888 > C'est du Termsyn.
Merci ![]()