2015-05-18 10 views
19

Mam interfejs PackedObject:metody domyślne Java jest wolniejsza niż w tym samym kodzie, ale w abstrakcyjnej klasie

public interface PackedObject { 
    int get(); 
    int sum(); 
    void setIndex(int index); 
    default int defaultSum() { 
     return get(); 
    } 
} 

klasa abstrakcyjna AbstractPackedObject:

public abstract class AbstractPackedObject implements PackedObject { 
    protected int index = 0; 
    protected int[] buffer; 

    public void setIndex(int index) { 
     this.index = index; 
    } 

    public void setBuffer(int[] buffer) { 
     this.buffer = buffer; 
    } 

    @Override 
    public int sum(){ 
     return get(); 
    } 
} 

A beton implementacja WrappedPackedObject:

public class WrappedPackedObject extends AbstractPackedObject implements PackedObject { 

    public WrappedPackedObject(int[] buffer) { 
     this.buffer = buffer; 
    } 

    @Override 
    public int get() { 
     return buffer[index]; 
    } 
} 

Porównywano z defaultSum i sum metody (urywek odniesienia JMH):

for (int i = 0; i < NB; i++) { 
     packedObject.setIndex(i); 
     value += packedObject.defaultSum(); 
    } 

    for (int i = 0; i < NB; i++) { 
     packedObject.setIndex(i); 
     value += packedObject.sum(); 
    } 

próbuję zrozumieć dlaczego sum benchmarker jest szybszy niż benchmark defaultSum przez współczynnik 1,7.

Zacząłem zagłębiać się w tajniki JIT. Wywołaj strony docelowe tylko jedną metodą, więc spodziewam się, że zostanie to zrobione. Wyjście druku inline jest następujący:

@ 25 com.github.nithril.PackedObject::defaultSum (7 bytes) inline (hot) 
\-> TypeProfile (479222/479222 counts) = com/github/nithril/WrappedPackedObject 
    @ 1 com.github.nithril.WrappedPackedObject::get (14 bytes) inline (hot) 
    @ 10 java.nio.DirectByteBuffer::getInt (15 bytes) inline (hot) 


@ 25 com.github.nithril.AbstractPackedObject::sum (5 bytes) inline (hot) 
    @ 1 com.github.nithril.WrappedPackedObject::get (14 bytes) inline (hot) 
    @ 10 java.nio.DirectByteBuffer::getInt (15 bytes) inline (hot) 

ja jeszcze nie rozumiem, dlaczego ta linia pojawia TypeProfile (479222/479222 counts) = com/github/nithril/WrappedPackedObject

tworzę dedicated project z powyższym kodzie. Benchmark jest wykonywany przy użyciu JMH.

Dzięki za pomoc.

EDIT 20.05.2015:

I uproszczenie kodu Java.

Wewnętrzna pętla benchSum jest dość prosta:

0x00007f1bb11afb84: add 0x10(%r10,%r8,4),%eax ;*iadd 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 50) 
0x00007f1bb11afb89: mov %r8d,0xc(%r12,%r11,8) ;*putfield index 
               ; - com.github.nithril.AbstractPackedObject::[email protected] (line 13) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 49) 
0x00007f1bb11afb8e: inc %r8d    ;*iinc 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 48) 
0x00007f1bb11afb91: cmp $0x2710,%r8d 
0x00007f1bb11afb98: jl  0x00007f1bb11afb84 

Wewnętrzna pętla benchDefaultSum jest bardziej skomplikowana z odczytu/zapisu indeksu i wewnątrz pętli wewnętrznej porównania tablicy związana. Jeszcze nie w pełni zrozumieć cel tego porównania ...

0x00007fcfdcf82cb8: mov %edx,0xc(%r12,%r11,8) ;*putfield index 
               ; - com.github.nithril.AbstractPackedObject::[email protected] (line 13) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 32) 
0x00007fcfdcf82cbd: mov 0xc(%r10),%r8d  ;*getfield index 
               ; - com.github.nithril.WrappedPackedObject::[email protected] (line 17) 
               ; - com.github.nithril.PackedObject::[email protected] (line 15) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 33) 
0x00007fcfdcf82cc1: cmp %r9d,%r8d 
0x00007fcfdcf82cc4: jae 0x00007fcfdcf82d1f ;*iaload 
               ; - com.github.nithril.WrappedPackedObject::[email protected] (line 17) 
               ; - com.github.nithril.PackedObject::[email protected] (line 15) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 33) 
0x00007fcfdcf82cc6: add 0x10(%rcx,%r8,4),%eax ;*iadd 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 33) 
0x00007fcfdcf82ccb: inc %edx    ;*iinc 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 31) 
0x00007fcfdcf82ccd: cmp $0x2710,%edx 
0x00007fcfdcf82cd3: jl  0x00007fcfdcf82cb8 ;*aload_2 
[...] 
0x00007fcfdcf82ce6: mov $0xffffffe4,%esi 
0x00007fcfdcf82ceb: mov %r10,0x8(%rsp) 
0x00007fcfdcf82cf0: mov %ebx,0x4(%rsp) 
0x00007fcfdcf82cf4: mov %r8d,0x10(%rsp) 
0x00007fcfdcf82cf9: xchg %ax,%ax 
0x00007fcfdcf82cfb: callq 0x00007fcfdcdea1a0 ; OopMap{rbp=NarrowOop [8]=Oop off=416} 
               ;*iaload 
               ; - com.github.nithril.WrappedPackedObject::[email protected] (line 17) 
               ; - com.github.nithril.PackedObject::[email protected] (line 15) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 33) 
               ; {runtime_call} 
0x00007fcfdcf82d00: callq 0x00007fcff1c94320 ;*iaload 
               ; - com.github.nithril.WrappedPackedObject::[email protected] (line 17) 
               ; - com.github.nithril.PackedObject::[email protected] (line 15) 
               ; - com.github.nithril.PackedObjectBench::[email protected] (line 33) 
               ; {runtime_call} 
[...] 
0x00007fcfdcf82d1f: mov %eax,(%rsp) 
0x00007fcfdcf82d22: mov %edx,%ebx 
0x00007fcfdcf82d24: jmp 0x00007fcfdcf82ce6 
+2

... Dlaczego masz abstrakcyjną klasę i interfejs? –

+3

Dla celów testu porównawczego. Rozpocząłem test porównawczy za pomocą interfejsu bez domyślnej metody.Następnie przeniosłem "sumę" z konkretnej klasy do metody domyślnej. Następnie dostrzegam różnicę między betonem a interfejsem. Aby zakończyć test, tworzę abstrakcyjną klasę z metodą 'sum' i interfejsem z' defaultSum' –

+0

O, widzę. Myślałem, że to aplikacja i nie mogłem wymyślić miejsca, w którym potrzebowałbyś (a nawet tak naprawdę) dwóch warstw abstrakcji. –

Odpowiedz

5

Wystarczy regurgitating informację, że wybrałem się przez pobieżnej lekturze listy hotspot-compiler-dev korespondencji, ale może to być brak analizy klasowej hierarchii dla domyślnych metod w interfejsach, co zapobiega devirtualizacji metod interfejsu.

Zobacz JDK Bug 8065760 i 6986483


Domyślam się, że choć metoda ta jest inlined nadal jest poprzedzone osłoną typu, który pobiera wyeliminowany przez CHA w abstrakcyjnej przypadku, ale nie dla metody interfejsu.

Drukowanie zoptymalizowanego zestawu (uważam, że JMH ma pewną flagę) może to potwierdzić.

+1

Tak się nie dzieje, ponieważ wynik polecenia '-XX: + PrintInlining' w pytaniu oryginalnym wyraźnie pokazuje, że domyślna metoda została pomyślnie wprowadzona. – apangin

+2

może nadal zawierać strażnik typu, który CHA wyeliminuje, myślę, że potrzeba informacji o profilowaniu typu w jego inling wydruku sugeruje tak samo. – the8472

+0

Dzięki za twój wkład, będę wyglądać w domu po pracy. Jakaś wskazówka na temat wyglądu strażnika w zespole? –

Powiązane problemy