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
... Dlaczego masz abstrakcyjną klasę i interfejs? –
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' –
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. –