J'essaie de comprendre le code natif généré à partir d'une boucle Java. Le code natif devrait être optimisé par le compilateur C2, mais sur mon exemple simple, il semble que certaines optimisations manquent.Quand l'optimisation de la prédiction de boucle Java est-elle déclenchée par le compilateur C2 JIT?
C'est la méthode Java j'ai écrit la base sur l'exemple minimal de https://wiki.openjdk.java.net/display/HotSpot/LoopPredication:
104 public static byte[] myLoop(int init, int limit, int stride, int scale, int offset, byte value, byte[] array) {
105 for (int i = init; i < limit; i += stride) {
106 array [ scale * i + offset] = value;
107 }
108 return array;
109 }
Ce sont les arguments donnés à la Java 8 Hotspot VM pour forcer la compilation C2:
-server
-XX:-TieredCompilation
-XX:CompileThreshold=5
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintAssembly
-XX:-UseCompressedOops
-XX:+LogCompilation
-XX:+TraceClassLoading
-XX:+UseLoopPredicate
-XX:+RangeCheckElimination
Cette est le code natif amd64 généré par C2 ('myLoop' est appelé au moins 10000 fois):
# {method} {0x00007fcb5088ef38} 'myLoop' '(IIIIIB[B)[B' in 'MyClass'
# parm0: rsi = int
# parm1: rdx = int
# parm2: rcx = int
# parm3: r8 = int
# parm4: r9 = int
# parm5: rdi = byte
# parm6: [sp+0x40] = '[B' (sp of caller)
0x00007fcd44ee9fe0: mov %eax,0xfffffffffffec000(%rsp)
0x00007fcd44ee9fe7: push %rbp
0x00007fcd44ee9fe8: sub $0x30,%rsp ;*synchronization entry
; - MyClass::[email protected] (line 105)
0x00007fcd44ee9fec: cmp %edx,%esi
0x00007fcd44ee9fee: jnl 0x7fcd44eea04a ;*if_icmplt
; - MyClass::[email protected] (line 105)
0x00007fcd44ee9ff0: mov 0x40(%rsp),%rax
0x00007fcd44ee9ff5: mov 0x10(%rax),%r10d ;*bastore
; - MyClass::[email protected] (line 106)
; implicit exception: dispatches to 0x00007fcd44eea051
0x00007fcd44ee9ff9: nopl 0x0(%rax) ;*aload
; - MyClass::[email protected] (line 106)
0x00007fcd44eea000: mov %esi,%ebx
0x00007fcd44eea002: imull %r8d,%ebx
0x00007fcd44eea006: add %r9d,%ebx ;*iadd
; - MyClass::[email protected] (line 106)
0x00007fcd44eea009: cmp %r10d,%ebx
0x00007fcd44eea00c: jnb 0x7fcd44eea02e ;*bastore
; - MyClass::[email protected] (line 106)
0x00007fcd44eea00e: add %ecx,%esi ;*iadd
; - MyClass::[email protected] (line 105)
0x00007fcd44eea010: movsxd %ebx,%r11
0x00007fcd44eea013: mov %dil,0x18(%rax,%r11) ; OopMap{rax=Oop off=56}
;*if_icmplt
; - MyClass::[email protected] (line 105)
0x00007fcd44eea018: test %eax,0xa025fe2(%rip) ; {poll}
0x00007fcd44eea01e: cmp %edx,%esi
0x00007fcd44eea020: jl 0x7fcd44eea000 ;*synchronization entry
; - MyClass::[email protected] (line 105)
0x00007fcd44eea022: add $0x30,%rsp
0x00007fcd44eea026: pop %rbp
0x00007fcd44eea027: test %eax,0xa025fd3(%rip) ; {poll_return}
0x00007fcd44eea02d: retq
0x00007fcd44eea02e: movabs $0x7fcca3c810a8,%rsi ; {oop(a 'java/lang/ArrayIndexOutOfBoundsException')}
0x00007fcd44eea038: movq $0x0,0x18(%rsi) ;*bastore
; - MyClass::[email protected] (line 106)
0x00007fcd44eea040: add $0x30,%rsp
0x00007fcd44eea044: pop %rbp
0x00007fcd44eea045: jmpq 0x7fcd44e529a0 ; {runtime_call}
0x00007fcd44eea04a: mov 0x40(%rsp),%rax
0x00007fcd44eea04f: jmp 0x7fcd44eea022
0x00007fcd44eea051: mov %edx,%ebp
0x00007fcd44eea053: mov %ecx,0x40(%rsp)
0x00007fcd44eea057: mov %r8d,0x44(%rsp)
0x00007fcd44eea05c: mov %r9d,(%rsp)
0x00007fcd44eea060: mov %edi,0x4(%rsp)
0x00007fcd44eea064: mov %rax,0x8(%rsp)
0x00007fcd44eea069: mov %esi,0x10(%rsp)
0x00007fcd44eea06d: mov $0xffffff86,%esi
0x00007fcd44eea072: nop
0x00007fcd44eea073: callq 0x7fcd44dea1a0 ; OopMap{[8]=Oop off=152}
;*aload
; - MyClass::[email protected] (line 106)
; {runtime_call}
0x00007fcd44eea078: callq 0x7fcd4dc47c50 ;*aload
; - MyClass::[email protected] (line 106)
; {runtime_call}
0x00007fcd44eea07d: hlt
0x00007fcd44eea07e: hlt
0x00007fcd44eea07f: hlt
Selon https://wiki.openjdk.java.net/display/HotSpot/LoopPredication, une optimisation, appelée "élimination de la gamme de tableaux", élimine les vérifications de la gamme de tableaux dans la boucle mais ajoute un prédicat de boucle avant la boucle. Il semble que cette optimisation n'ait pas été faite sur 'myLoop' par C2. est à 0x7fcd44eea020 et saute à 0x7fcd44eea000 saut en arrière de la boucle. Dans la boucle, il y a toujours un contrôle de plage à 0x7fcd44eea009-0x7fcd44eea00c.
- Pourquoi y a-t-il encore une vérification dans la boucle?
- Pourquoi l'optimisation de la prédiction de boucle n'a-t-elle pas été exécutée?
- Comment puis-je forcer toutes les optimisations?
Aussi, vous n'avez pas besoin de spécifier '' UseLoopPredicate' et drapeaux RangeCheckElimination' - ils sont activés par défaut. – apangin