Java 默认方法比抽象类中的相同代码慢

2024-01-19

我有一个界面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 and 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

我创建一个专用项目 https://github.com/nithril/packedobject-test与上面的代码。基准测试是使用 JMH 完成的。

感谢您的帮助。

编辑2015/05/20:

我简化了java代码。

的内循环benchSum非常简单:

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

的内循环benchDefaultSum索引的读/写以及内部循环内数组绑定的比较更加复杂。我还没有完全理解这种比较的目的......

0x00007fcfdcf82cb8: mov    %edx,0xc(%r12,%r11,8)  ;*putfield index
                                              ; - com.github.nithril.AbstractPackedObject::setIndex@2 (line 13)
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@17 (line 32)
0x00007fcfdcf82cbd: mov    0xc(%r10),%r8d     ;*getfield index
                                              ; - com.github.nithril.WrappedPackedObject::get@5 (line 17)
                                              ; - com.github.nithril.PackedObject::defaultSum@1 (line 15)
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33)
0x00007fcfdcf82cc1: cmp    %r9d,%r8d
0x00007fcfdcf82cc4: jae    0x00007fcfdcf82d1f  ;*iaload
                                              ; - com.github.nithril.WrappedPackedObject::get@8 (line 17)
                                              ; - com.github.nithril.PackedObject::defaultSum@1 (line 15)
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33)
0x00007fcfdcf82cc6: add    0x10(%rcx,%r8,4),%eax  ;*iadd
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@29 (line 33)
0x00007fcfdcf82ccb: inc    %edx               ;*iinc
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@31 (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::get@8 (line 17)
                                              ; - com.github.nithril.PackedObject::defaultSum@1 (line 15)
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33)
                                              ;   {runtime_call}
0x00007fcfdcf82d00: callq  0x00007fcff1c94320  ;*iaload
                                              ; - com.github.nithril.WrappedPackedObject::get@8 (line 17)
                                              ; - com.github.nithril.PackedObject::defaultSum@1 (line 15)
                                              ; - com.github.nithril.PackedObjectBench::benchDefaultSum@24 (line 33)
                                              ;   {runtime_call}
[...]
0x00007fcfdcf82d1f: mov    %eax,(%rsp)
0x00007fcfdcf82d22: mov    %edx,%ebx
0x00007fcfdcf82d24: jmp    0x00007fcfdcf82ce6

只是反省我通过粗略阅读而获得的信息热点编译器开发 http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2015-April/thread.html#17649邮件列表,但这可能缺少类层次分析用于接口中的默认方法,这可以防止接口方法的去虚拟化。

See JDK 错误 8065760 https://bugs.openjdk.java.net/browse/JDK-8065760 and 6986483 https://bugs.openjdk.java.net/browse/JDK-6986483


我的猜测是,即使该方法是内联的,它前面仍然有一个类型保护,在抽象情况下该类型保护被 CHA 消除,但对于接口方法则不然。

打印优化的程序集(我认为 JMH 有一些标志)可以证实这一点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 默认方法比抽象类中的相同代码慢 的相关文章

随机推荐