草庐IT

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

coder 2023-05-19 原文

我有一个接口(interface)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];
    }
}

我对 defaultSumsum 方法进行了基准测试(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

我创建了一个 dedicated project用上面的代码。基准测试是使用 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 的内部循环更复杂,因为索引的读/写和内部循环内部数组绑定(bind)的比较。我还没有完全理解这个比较的目的......

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

最佳答案

我只是通过粗略阅读 hotspot-compiler-dev 获得的反刍信息邮件列表,但这可能是接口(interface)中的默认方法缺少类层次结构分析,这阻碍了接口(interface)方法的去虚拟化。

JDK Bug 80657606986483


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

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

关于Java 默认方法比相同的代码慢,但在抽象类中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30312096/

有关Java 默认方法比相同的代码慢,但在抽象类中的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby - 默认情况下使选项为 false - 2

    这是在Ruby中设置默认值的常用方法:classQuietByDefaultdefinitialize(opts={})@verbose=opts[:verbose]endend这是一个容易落入的陷阱:classVerboseNoMatterWhatdefinitialize(opts={})@verbose=opts[:verbose]||trueendend正确的做法是:classVerboseByDefaultdefinitialize(opts={})@verbose=opts.include?(:verbose)?opts[:verbose]:trueendend编写Verb

  4. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  5. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  6. ruby-on-rails - 在默认方法参数中使用 .reverse_merge 或 .merge - 2

    两者都可以defsetup(options={})options.reverse_merge:size=>25,:velocity=>10end和defsetup(options={}){:size=>25,:velocity=>10}.merge(options)end在方法的参数中分配默认值。问题是:哪个更好?您更愿意使用哪一个?在性能、代码可读性或其他方面有什么不同吗?编辑:我无意中添加了bang(!)...并不是要询问nobang方法与bang方法之间的区别 最佳答案 我倾向于使用reverse_merge方法:option

  7. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  8. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  9. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  10. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

随机推荐