这是什么的OpenJDK线的优点数1455.
代码段:
private final char value[];
// ...
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value; // <--- this line
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
Run Code Online (Sandbox Code Playgroud)
请注意,虽然参照private final char value[]
被复制到本地val
的循环内访问,其.length
领域还在通过访问value
,不是val
.
我怀疑" 性能 "是答案(例如,从本地读取比从现场读取更快)但我会欣赏一个精确且易于阅读的答案,甚至可能有一些关于优势的数据.
我查看了字节码,正如@user 评论的那样,这可能是一种避免getfield
循环内调用的优化。但他们搞砸了,仍然在循环条件中引用 value 变量......所以这实际上使字节码更长更慢。
public int h1() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
public int h2() {
int h = hash;
if (h == 0 && value.length > 0) {
for (int i = 0; i < value.length; i++) {
h = 31 * h + value[i];
}
hash = h;
}
return h;
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到这两种方法生成几乎相同的字节码,但是我们的“优化”实现实际上最终使用了另外 2 个调用。
请注意 for 循环测试(h1 中的第 39-45 行和 h2 中的第 37-43 行)如何调用 getfield 来执行数组长度调用。
字节码:
public int h1();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 53
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 53
17: aload_0
18: getfield #15 // Field value:[C
21: astore_2
22: iconst_0
23: istore_3
24: goto 39
27: bipush 31
29: iload_1
30: imul
31: aload_2
32: iload_3
33: caload
34: iadd
35: istore_1
36: iinc 3, 1
39: iload_3
40: aload_0
41: getfield #15 // Field value:[C
44: arraylength
45: if_icmplt 27
48: aload_0
49: iload_1
50: putfield #17 // Field hash:I
53: iload_1
54: ireturn
public int h2();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 51
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 51
17: iconst_0
18: istore_2
19: goto 37
22: bipush 31
24: iload_1
25: imul
26: aload_0
27: getfield #15 // Field value:[C
30: iload_2
31: caload
32: iadd
33: istore_1
34: iinc 2, 1
37: iload_2
38: aload_0
39: getfield #15 // Field value:[C
42: arraylength
43: if_icmplt 22
46: aload_0
47: iload_1
48: putfield #17 // Field hash:I
51: iload_1
52: ireturn
Run Code Online (Sandbox Code Playgroud)
如果他们更改了循环条件以也使用本地字段,
...
for (int i = 0; i < val.length; i++) {
...
Run Code Online (Sandbox Code Playgroud)
然后字节码实际上变得更短并且丢失了循环中可能更慢的 getfield 调用,
public int h1();
Code:
0: aload_0
1: getfield #17 // Field hash:I
4: istore_1
5: iload_1
6: ifne 50
9: aload_0
10: getfield #15 // Field value:[C
13: arraylength
14: ifle 50
17: aload_0
18: getfield #15 // Field value:[C
21: astore_2
22: iconst_0
23: istore_3
24: goto 39
27: bipush 31
29: iload_1
30: imul
31: aload_2
32: iload_3
33: caload
34: iadd
35: istore_1
36: iinc 3, 1
39: iload_3
40: aload_2
41: arraylength
42: if_icmplt 27
45: aload_0
46: iload_1
47: putfield #17 // Field hash:I
50: iload_1
51: ireturn
Run Code Online (Sandbox Code Playgroud)
在我的 jdk 1.7.0_79 上对方法进行几百万次循环的愚蠢测试一致表明,原始方法的运行时间比未“优化”或正确“优化”的方法要长大约 5 倍。后两者在性能上没有差异。
所以我想总而言之,在本地存储该字段是一种优化方法字节码的尝试,可能是在 jit 能够优化它本身之前,但他们把它搞砸了,实际上使该方法变得更糟......
该代码实际上已在 Java 9 中修复, https://bugs.openjdk.java.net/browse/JDK-8058643
归档时间: |
|
查看次数: |
258 次 |
最近记录: |