为什么哈希码比类似的方法慢?更新更新 2更新 3
通常,Java 根据给定调用端遇到的实现数量来优化虚拟调用。这可以在我的基准测试结果中很容易看出,当你看,这是一个返回存储的微不足道的方法。有一个微不足道的myCode
int
static abstract class Base {
abstract int myCode();
}
有几个相同的实现,如
static class A extends Base {
@Override int myCode() {
return n;
}
@Override public int hashCode() {
return n;
}
private final int n = nextInt();
}
随着实现数量的增加,方法调用的时间从两个实现的 0.4 ns 到 1.2 ns 增长到 11.6 ns,然后增长缓慢。当JVM已经看到多个实现时,即时间略有不同(因为需要测试)。preload=true
instanceof
到目前为止,一切都很清楚,但是,行为却大不相同。特别是,在三种情况下,它的速度要慢8-10倍。任何想法为什么?hashCode
更新
我很好奇穷人是否可以通过手动调度得到帮助,而且可以很多。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();
}
请注意,编译器避免对两个以上的实现进行此类优化,因为大多数方法调用比简单的字段加载昂贵得多,并且与代码膨胀相比,增益很小。
最初的问题“为什么JIT不像其他方法那样优化哈希码
”仍然存在,并证明它确实可以。hashCode2
更新 2
看起来最好的是正确的,至少有这个音符
调用任何扩展 Base 的类的 hashCode() 都与调用 Object.hashCode() 相同,如果你在 Base 中添加一个显式的 hashCode,这将限制调用 Base.hashCode() 的潜在调用目标,这就是它在字节码中编译的方式。
我不完全确定发生了什么,但宣布再次具有竞争力。Base.hashCode()
hashCode
更新 3
好的,提供具体的帮助实现,但是,JIT必须知道它永远不会被调用,因为所有子类都定义了自己的子类(除非加载另一个子类,这可能导致去优化,但这对JIT来说并不是什么新鲜事)。Base#hashCode
所以它看起来像是错过了优化机会#1。
提供抽象的实现的工作原理相同。这是有道理的,因为它提供了确保不需要进一步的查找,因为每个子类都必须提供自己的(它们不能简单地从祖父母那里继承)。Base#hashCode
对于两个以上的实现,仍然快得多,以至于编译器必须做一些次优的事情。也许是错过了优化机会#2?myCode