Подтвердить что ты не робот

Методы Java по умолчанию медленнее, чем один и тот же код, но в абстрактном классе

У меня есть интерфейс PackedObject:

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

Абстрактный класс 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();
    }
}

И конкретная реализация WrappedPackedObject:

public class WrappedPackedObject extends AbstractPackedObject implements PackedObject {

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

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

Я сравнивал методы defaultSum и sum (фрагмент теста 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();
    }

Я пытаюсь понять, почему эталонный тест sum быстрее, чем тест defaultSum в 1,7 раза.

Я начал копаться в тайне JIT. Вызывающий сайт нацелен только на один метод, поэтому я ожидаю, что вложение будет выполнено. Результатом печати является следующее:

@ 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)

Я еще не понимаю, почему эта строка появляется TypeProfile (479222/479222 counts) = com/github/nithril/WrappedPackedObject

Я создаю выделенный проект с указанным выше кодом. Тест выполняется с использованием JMH.

Спасибо за вашу помощь.

РЕДАКТИРОВАТЬ 2015/05/20:

Я упрощаю код Java.

Внутренний цикл benchSum довольно прост:

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

Внутренний цикл benchDefaultSum более сложный с чтением/записью индекса и внутри внутреннего цикла сравнение привязки массива. Я еще не совсем понимаю цель этого сравнения...

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
4b9b3361

Ответ 1

Просто срывая информацию, которую я просмотрел путем поверхностного чтения списка рассылки hotspot-compiler-dev, но это может быть отсутствие класса иерархический анализ методов по умолчанию в интерфейсах, который предотвращает девиртуализацию методов интерфейса.

См. JDK Bug 8065760 и 6986483


Мое предположение заключается в том, что, несмотря на то, что метод встроен, ему все еще предшествует защита типа, которая исключается CHA в абстрактном случае, но не для метода интерфейса.

Печать оптимизированной сборки (я думаю, что JMH имеет некоторый флаг для этого) может подтвердить это.