我正在查看 JDK(JDK 12,但它也适用于旧版本)代码并发现了一些奇怪的结构,我不明白为什么要使用它们。让我们举个例子Map.computeIfPresent,因为它很简单:
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Object oldValue;
if ((oldValue = this.get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
if (newValue != null) {
this.put(key, newValue);
return newValue;
} else {
this.remove(key);
return null;
}
} else {
return null;
}
}
这个结构if ((oldValue = this.get(key)) != null)让我很吃惊。我知道这是可能的,因为它没什么特别的,但在正常的生产代码中我会认为它是一种代码味道。为什么不直接写成正常的方式(Object oldValue = this.get(key))?一定是一些离合器优化,这就是我的想法。
写了一个较小的版本来检查字节码:
int computeIfPresent(int key) {
Integer oldValue;
if ((oldValue = get(key)) != null) {
return oldValue;
} else {
return 2;
}
}
字节码输出:
int computeIfPresent(int);
Code:
0: aload_0
1: iload_1
2: invokevirtual #2 // Method get:(I)Ljava/lang/Integer;
5: dup
6: astore_2
7: ifnull 15
10: aload_2
11: invokevirtual #3 // Method java/lang/Integer.intValue:()I
14: ireturn
15: iconst_2
16: ireturn
具有经典变量初始化的“普通”版本的字节码:
int computeIfPresent(int);
Code:
0: aload_0
1: iload_1
2: invokevirtual #2 // Method get:(I)Ljava/lang/Integer;
5: astore_2
6: aload_2
7: ifnull 15
10: aload_2
11: invokevirtual #3 // Method java/lang/Integer.intValue:()I
14: ireturn
15: iconst_2
16: ireturn
唯一的区别是dup + astore_2vs。astore_2 + aload_2我什至怀疑第一个“离合器优化”版本更糟,因为dup它被使用并且堆栈无缘无故地更大。也许我的例子太简单了,优化在更复杂的上下文中扩展了很多。
这是一个简单的例子,绝对不是 JDK 代码中的一个例子——打开HashMap.java,有很多这样的片段,有时在同一行有多个:
if ((first = tab[i = (n - 1) & hash]) != null)
尽管它真的很简单,但由于这些结构,我不得不停下来想一想这段代码实际上做了什么。
使用这些结构背后的真正原因是什么?我敢肯定这不仅仅是糟糕的代码。在我看来,代码质量受到很大影响,因此收益一定是可观的。或者只是规则leave small optimizations to JIT不适用于 JDK,因为它必须尽可能多地压缩性能?
或者这只是规则的极端initialize variables as late as possible?:)
慕哥9229398
相关分类