job*_*job 14 java optimization micro-optimization
我一直在寻找一些Java基本集合(的宝库,fastutil,HPPC)和我已经注意到,类变量有时声明为图案final的局部变量.例如:
public void forEach(IntIntProcedure p) {
final boolean[] used = this.used;
final int[] key = this.key;
final int[] value = this.value;
for (int i = 0; i < used.length; i++) {
if (used[i]) {
p.apply(key[i],value[i]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已经完成了一些基准测试,看起来这样做会稍快一点,但为什么会这样呢?我试图理解如果函数的前三行被注释掉,Java将采取哪些不同的做法.
注意:这似乎与这个问题类似,但这是针对c ++的,并没有说明它们被声明的原因final.
Tom*_*icz 26
访问局部变量或参数是一步操作:获取位于堆栈上偏移量N的变量.如果你的函数有2个参数(简化):
this因此,当您访问局部变量时,您有一个固定偏移量的内存访问(N在编译时已知).这是用于访问第一个方法参数(int)的字节码:
iload 1 //N = 1
Run Code Online (Sandbox Code Playgroud)
但是,当您访问字段时,您实际上正在执行额外的步骤.首先,您只是为了确定当前的对象地址而读取" 局部变量 " this.然后你加载一个getfield具有固定偏移量的field()this.因此,您执行两个内存操作而不是一个(或一个额外的).字节码:
aload 0 //N = 0: this reference
getfield total I //int total
Run Code Online (Sandbox Code Playgroud)
因此,技术上访问局部变量和参数比对象字段更快.实际上,许多其他因素可能会影响性能(包括各种级别的CPU缓存和JVM优化).
final是一个不同的故事.它基本上是编译器/ JIT的一个提示,这个引用不会改变,所以它可以做一些更重的优化.但这很难追踪,尽可能使用经验法则final.
该final关键字是这里的红鲱鱼.性能差异之所以出现是因为他们说了两件不同的事情.
public void forEach(IntIntProcedure p) {
final boolean[] used = this.used;
for (int i = 0; i < used.length; i++) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
是说,"取一个布尔数组,并为每个元素是数组做一些事情."
如果没有final boolean[] used,该函数会说"当索引小于used当前对象字段当前值的长度时,获取当前对象字段的当前值并对used索引处的元素执行某些操作i."
JIT可能更容易证明循环绑定不变量以消除超额绑定检查等等,因为它可以更容易地确定导致值used变化的原因.即使忽略多个线程,如果p.apply可以改变值,used那么JIT也不能消除边界检查或做其他有用的优化.
| 归档时间: |
|
| 查看次数: |
5412 次 |
| 最近记录: |