Wie andere betont haben, ist der Test in vielerlei Hinsicht fehlerhaft.
Sie haben uns nicht genau gesagt, wie Sie diesen Test durchgeführt haben. Ich habe jedoch versucht, einen "naiven" Test (keine Beleidigung) wie folgt durchzuführen:
class PrePostIncrement
{
public static void main(String args[])
{
for (int j=0; j<3; j++)
{
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runPreIncrement();
long after = System.nanoTime();
System.out.println("pre : "+(after-before)/1e6);
}
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runPostIncrement();
long after = System.nanoTime();
System.out.println("post : "+(after-before)/1e6);
}
}
}
private static void runPreIncrement()
{
final int n = Integer.MAX_VALUE;
int i = 0;
while (++i < n) {}
}
private static void runPostIncrement()
{
final int n = Integer.MAX_VALUE;
int i = 0;
while (i++ < n) {}
}
}
Wenn Sie dies mit Standardeinstellungen ausführen, scheint es einen kleinen Unterschied zu geben. Der wahre Fehler des Benchmarks wird jedoch offensichtlich, wenn Sie dies mit der -server
Flagge ausführen . Die Ergebnisse in meinem Fall sind dann ungefähr so
...
pre : 6.96E-4
pre : 6.96E-4
pre : 0.001044
pre : 3.48E-4
pre : 3.48E-4
post : 1279.734543
post : 1295.989086
post : 1284.654267
post : 1282.349093
post : 1275.204583
Offensichtlich wurde die Pre-Inkrement-Version komplett weg optimiert . Der Grund ist ziemlich einfach: Das Ergebnis wird nicht verwendet. Es spielt überhaupt keine Rolle, ob die Schleife ausgeführt wird oder nicht, daher entfernt die JIT sie einfach.
Dies wird durch einen Blick auf die Hotspot-Demontage bestätigt: Die Pre-Inkrement-Version führt zu folgendem Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055060500} 'runPreIncrement' '()V' in 'PrePostIncrement'
# [sp+0x20] (sp of caller)
0x000000000286fd80: sub $0x18,%rsp
0x000000000286fd87: mov %rbp,0x10(%rsp) ;*synchronization entry
; - PrePostIncrement::runPreIncrement@-1 (line 28)
0x000000000286fd8c: add $0x10,%rsp
0x000000000286fd90: pop %rbp
0x000000000286fd91: test %eax,-0x243fd97(%rip) # 0x0000000000430000
; {poll_return}
0x000000000286fd97: retq
0x000000000286fd98: hlt
0x000000000286fd99: hlt
0x000000000286fd9a: hlt
0x000000000286fd9b: hlt
0x000000000286fd9c: hlt
0x000000000286fd9d: hlt
0x000000000286fd9e: hlt
0x000000000286fd9f: hlt
Die Post-Inkrement-Version führt zu folgendem Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x00000000550605b8} 'runPostIncrement' '()V' in 'PrePostIncrement'
# [sp+0x20] (sp of caller)
0x000000000286d0c0: sub $0x18,%rsp
0x000000000286d0c7: mov %rbp,0x10(%rsp) ;*synchronization entry
; - PrePostIncrement::runPostIncrement@-1 (line 35)
0x000000000286d0cc: mov $0x1,%r11d
0x000000000286d0d2: jmp 0x000000000286d0e3
0x000000000286d0d4: nopl 0x0(%rax,%rax,1)
0x000000000286d0dc: data32 data32 xchg %ax,%ax
0x000000000286d0e0: inc %r11d ; OopMap{off=35}
;*goto
; - PrePostIncrement::runPostIncrement@11 (line 36)
0x000000000286d0e3: test %eax,-0x243d0e9(%rip) # 0x0000000000430000
;*goto
; - PrePostIncrement::runPostIncrement@11 (line 36)
; {poll}
0x000000000286d0e9: cmp $0x7fffffff,%r11d
0x000000000286d0f0: jl 0x000000000286d0e0 ;*if_icmpge
; - PrePostIncrement::runPostIncrement@8 (line 36)
0x000000000286d0f2: add $0x10,%rsp
0x000000000286d0f6: pop %rbp
0x000000000286d0f7: test %eax,-0x243d0fd(%rip) # 0x0000000000430000
; {poll_return}
0x000000000286d0fd: retq
0x000000000286d0fe: hlt
0x000000000286d0ff: hlt
Mir ist nicht ganz klar, warum die Post-Inkrement-Version anscheinend nicht entfernt wird. (Tatsächlich betrachte ich dies als separate Frage). Aber zumindest erklärt dies, warum Sie möglicherweise Unterschiede mit einer "Größenordnung" sehen ...
EDIT: Interessant ist , wenn die obere Grenze der Schleife ändert , Integer.MAX_VALUE
um Integer.MAX_VALUE-1
dann beide sind Versionen wegoptimiert und erfordern „Null“ der Zeit. Irgendwie verhindert diese Grenze (die immer noch wie 0x7fffffff
in der Baugruppe erscheint) die Optimierung. Vermutlich hat dies etwas damit zu tun, dass der Vergleich einer (versengten!) cmp
Anweisung zugeordnet wird, aber darüber hinaus kann ich keinen tiefgreifenden Grund nennen. Die JIT arbeitet auf mysteriöse Weise ...