为什么hashCode比类似的方法慢?

maa*_*nus 36 java performance hashcode

通常,Java会根据给定调用端遇到的实现数量来优化虚拟调用.当你看一下,这可以很容易地在我的基准测试结果中看到,这是一个返回存储的简单方法.这是微不足道的myCodeint

static abstract class Base {
    abstract int myCode();
}
Run Code Online (Sandbox Code Playgroud)

与几个完全相同的实现

static class A extends Base {
    @Override int myCode() {
        return n;
    }
    @Override public int hashCode() {
        return n;
    }
    private final int n = nextInt();
}
Run Code Online (Sandbox Code Playgroud)

随着实现数量的增加,方法调用的时序从两个实现的0.4 ns增加到1.2 ns,再增长到11.6 ns,然后缓慢增长.当JVM看到多个实现时,preload=true即时序略有不同(因为instanceof需要进行测试).

到目前为止,一切都很清楚,但hashCode行为却相当不同.特别是,在三种情况下,它慢了8-10倍.知道为什么吗?

UPDATE

我很好奇,如果hashCode可以通过手动调度来帮助穷人,那可能会很多.

定时

几个分支完美地完成了这项工作:

if (o instanceof A) {
    result += ((A) o).hashCode();
} else if (o instanceof B) {
    result += ((B) o).hashCode();
} else if (o instanceof C) {
    result += ((C) o).hashCode();
} else if (o instanceof D) {
    result += ((D) o).hashCode();
} else { // Actually impossible, but let's play it safe.
    result += o.hashCode();
}
Run Code Online (Sandbox Code Playgroud)

请注意,编译器避免了对两个以上实现的这种优化,因为大多数方法调用比简单的字段加载要昂贵得多,并且与代码膨胀相比,增益会很小.

最初的问题" 为什么JIT不优化hashCode其他方法 "仍然存在并hashCode2证明它确实可以.

更新2

看起来好极了,至少在这个说明中

调用任何扩展Base的类的hashCode()与调用Object.hashCode()相同,这就是它在字节码中编译的方式,如果你在Base中添加一个显式hashCode来限制潜在的调用目标调用Base.hashCode() .

我不完全确定发生了什么,但宣布再次Base.hashCode()具有hashCode竞争力.

结果2

更新3

好的,提供帮助的具体实现Base#hashCode,但是,JIT必须知道它永远不会被调用,因为所有子类都定义了它们(除非另外一个子类被加载,这可能导致去优化,但这对于JIT来说并不新鲜) .

所以它看起来像错过了优化机会#1.

提供相同的抽象实现Base#hashCode.这是有道理的,因为它提供了确保不需要进一步查找,因为每个子类必须提供它自己的(它们不能简单地从它们的祖父母继承).

仍然有两个以上的实现,myCode速度要快得多,编译器必须做一些不理想的事情.也许错过了优化机会#2?

Eri*_*las -2

hashCode() 的语义比常规方法更复杂,因此当您调用 hashCode() 时,JVM 和 JIT 编译器必须比调用常规虚拟方法时执行更多的工作。

一种特殊性会对性能产生负面影响:在空对象上调用 hashCode() 是有效的并返回零。这需要比常规调用多一个分支,这本身就可以解释您所说的性能差异。

请注意,由于引入了具有此语义的 Object.hashCode(target),这似乎只是从 Java 7 开始的。了解您在哪个版本上测试了此问题以及您在 Java6 上是否有相同的情况将会很有趣。

另一个特性对性能有积极的影响:如果您不提供自己的 hasCode() 实现,JIT 编译器将使用内联哈希码计算代码,该代码比常规编译的 Object.hashCode 调用更快。

E.

  • 你错了。1. `null.hashCode()` 与任何其他方法一样抛出。2. `Objects` 是一个实用程序类,不要与 `Object` 混淆。3. 不提供“hashCode”意味着继承自标记为“native”的“Object.hashCode”,但在内部(可能)委托给“System.identityHashCode”。4.这不是JIT,只是普通的继承。5. 在基准测试中,`myCode` 类似地继承自 `Base`;`Base.myCode` 是抽象的,但这没有改变 (4认同)