Ici tu as des mecs qui ont codé un peu plus que des TP d'IUT; qui sont certainement capable d'écrire du C bien mieux que toi et qui en plus sont compétents en Java. Tu penses pas que tu devrais réfléchir un peu ? De plus tu découvriras aussi le compromis plus tard. Si je peux faire 30% plus rapide mais que ca demande 50% de budget en plus est ce que j'y gagne ? La rapidité n'est qu'un critère il y en a de nombreux autres, techniques ou pas.
Revenons à nos moutons, tu ne comprends pas que le code que tu as pondu (et qu'on a toujours pas vu) doit cumuler les cas pathologiques qui ne se retrouvent pas dans une vraie appli ? Tu ne comprends pas pourquoi ton test est mauvais ? Il n'y a que des imbéciles qui disent que Java est facile. Il faut apprendre comment ca marche. Certains concepts sont propres à l'OOP, d'autres à a spécification de la JVM, du langage Java ou à une implémentation particulière. Il faut les maitriser, et c'est parreil avec toutes les technos. Par contre java n'est pas piégieur (l'API de la libc est ultra casse gueule par exemple).
Tu parles tu temps d'appel d'une fonction ok, mais c'est quoi un appel de fonction ? Quelle est la sémantique en C et en Java ? A ton avis tout les appels de méthodes suivants sont ils équivalents ?
private void a() {}
public void b() {}
public final void c() {}
public static void d() {}
La réponse est bien évidement non. X.b() est un appel de méthode virtuel alors que tout les autres seront des appels finaux. Dans le cas d'un appel virtuel tu as des opérations supplémentaires pour déterminer le type réel de l'objet et appeler la bonne méthode:
Ce n'est pas si couteux que ca mais sur un microbench tu peux le sentir. Contrairement au C++, en Java les méthodes sont virtuelles sauf si private/static/final. La JVM est tout de même assez intelligente, ici elle se passe d'un call,dynamic et préfere deux call,static conditionnels. De plus si une classe n'est pas sous classée ca devient un appel non virtuel:
014 B2: # B7 B3 <- B1 Freq: 0.999999
014 nop # 3 bytes pad for loops and calls
017 call,static Virt::b
# Main::doit @ bci:1 L[0]=RBP
# OopMap{rbp=Oop off=28}
Et comme Java supporte le dynamic code loading ça peut changer au cours de l'exécution, une autre beauté d'Hotspot...
Ce n'est qu'un exemple de pourquoi ce genre de microbench peut être très trompeur si tu ne maitrise pas parfaitement ton sujet tu peux mesurer deux choses très différentes sans t'en rendre compte.
Maintenant qu'on a vu l'"appel de méthode". Passons à ton bench. Supposons que tu a écris une fonction tail recursive, comme factoriel. Compilé par gcc ça donne ça (-O2):
C'est dommage tu voulais bencher les appels de méthode mais y'en a pas ! L'opti qui est possible en C n'est pas possible en Java. Donc ton bench il sert à quoi ? Tu enfonces une porte ouverte, un jump est plus rapide qu'un callq et toutes les opérations associées. Écrire un tel code en Java c'est une faute, il faut utiliser la version impérative tu auras exactement les mêmes perfs qu'en C ou assembleur.
Je passe sur le fait que ton bench a peu de chances d'être compilé par le JIT étant donné la structure de ton test.
Mais au final tout ca on s'en fou. Hormis si tu bosses dans quelques secteurs bien précis, du code CPU bound t'en écriras pas des masses... Et c'est pas ca qui est important ou qui fera ramer un projet, ça serait trop simple.
[^] # Re: Ça passe très bien :)
Posté par ckyl . En réponse à la dépêche Jython supporte maintenant Python 2.5. Évalué à 8.
Revenons à nos moutons, tu ne comprends pas que le code que tu as pondu (et qu'on a toujours pas vu) doit cumuler les cas pathologiques qui ne se retrouvent pas dans une vraie appli ? Tu ne comprends pas pourquoi ton test est mauvais ? Il n'y a que des imbéciles qui disent que Java est facile. Il faut apprendre comment ca marche. Certains concepts sont propres à l'OOP, d'autres à a spécification de la JVM, du langage Java ou à une implémentation particulière. Il faut les maitriser, et c'est parreil avec toutes les technos. Par contre java n'est pas piégieur (l'API de la libc est ultra casse gueule par exemple).
Tu parles tu temps d'appel d'une fonction ok, mais c'est quoi un appel de fonction ? Quelle est la sémantique en C et en Java ? A ton avis tout les appels de méthodes suivants sont ils équivalents ?
private void a() {}
public void b() {}
public final void c() {}
public static void d() {}
La réponse est bien évidement non. X.b() est un appel de méthode virtuel alors que tout les autres seront des appels finaux. Dans le cas d'un appel virtuel tu as des opérations supplémentaires pour déterminer le type réel de l'objet et appeler la bonne méthode:
010 B2: # B6 B3 <- B1 Freq: 0.999999
010 movq R11, precise klass Virt: 0x0000000000d5b7d8:Constant:exact * # ptr
01a cmpq R10, R11 # ptr
01d je,us B6 P=0.500000 C=-1.000000
01d
01f B3: # B11 B4 <- B2 Freq: 0.499999
01f movq R11, precise klass Virt2: 0x0000000000d5b9e8:Constant:exact * # ptr
029 cmpq R10, R11 # ptr
02c jne,us B11 P=0.000001 C=-1.000000
02c
02e B4: # B12 B5 <- B3 Freq: 0.499999
02e movq RBP, RSI # spill
031 # checkcastPP of RBP
031 movq RSI, RBP # spill
034 nop # 3 bytes pad for loops and calls
037 call,static Virt2::b
# Main::doit @ bci:1 L[0]=RBP
# OopMap{rbp=Oop off=60}
03c
03c B5: # B8 <- B4 Freq: 0.499989
# Block is sole successor of call
03c movq RSI, RBP # spill
03f jmp,s B8
03f
041 B6: # B13 B7 <- B2 Freq: 0.499999
041 movq RBP, RSI # spill
044 # checkcastPP of RBP
044 movq RSI, RBP # spill
047 call,static Virt::b
# Main::doit @ bci:1 L[0]=RBP
# OopMap{rbp=Oop off=76}
04c
Ce n'est pas si couteux que ca mais sur un microbench tu peux le sentir. Contrairement au C++, en Java les méthodes sont virtuelles sauf si private/static/final. La JVM est tout de même assez intelligente, ici elle se passe d'un call,dynamic et préfere deux call,static conditionnels. De plus si une classe n'est pas sous classée ca devient un appel non virtuel:
014 B2: # B7 B3 <- B1 Freq: 0.999999
014 nop # 3 bytes pad for loops and calls
017 call,static Virt::b
# Main::doit @ bci:1 L[0]=RBP
# OopMap{rbp=Oop off=28}
Et comme Java supporte le dynamic code loading ça peut changer au cours de l'exécution, une autre beauté d'Hotspot...
Ce n'est qu'un exemple de pourquoi ce genre de microbench peut être très trompeur si tu ne maitrise pas parfaitement ton sujet tu peux mesurer deux choses très différentes sans t'en rendre compte.
Maintenant qu'on a vu l'"appel de méthode". Passons à ton bench. Supposons que tu a écris une fonction tail recursive, comme factoriel. Compilé par gcc ça donne ça (-O2):
0000000000400480 <fact>:
400480: 85 ff test %edi,%edi
400482: b8 01 00 00 00 mov 0ドルx1,%eax
400487: 74 0f je 400498 <fact+0x18>
400489: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
400490: 0f af c7 imul %edi,%eax
400493: 83 ef 01 sub 0ドルx1,%edi
400496: 75 f8 jne 400490 <fact+0x10>
400498: f3 c3 repz retq
40049a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
C'est dommage tu voulais bencher les appels de méthode mais y'en a pas ! L'opti qui est possible en C n'est pas possible en Java. Donc ton bench il sert à quoi ? Tu enfonces une porte ouverte, un jump est plus rapide qu'un callq et toutes les opérations associées. Écrire un tel code en Java c'est une faute, il faut utiliser la version impérative tu auras exactement les mêmes perfs qu'en C ou assembleur.
Je passe sur le fait que ton bench a peu de chances d'être compilé par le JIT étant donné la structure de ton test.
Mais au final tout ca on s'en fou. Hormis si tu bosses dans quelques secteurs bien précis, du code CPU bound t'en écriras pas des masses... Et c'est pas ca qui est important ou qui fera ramer un projet, ça serait trop simple.