用于循环优化的Java ArrayList
在Dov Bulka的Java性能和可伸缩性:第1卷中,作者提到在用于循环优化的Java ArrayList,java,for-loop,optimization,arraylist,Java,For Loop,Optimization,Arraylist,在Dov Bulka的Java性能和可伸缩性:第1卷中,作者提到在ArrayList上循环 for (int i = 0; i < vector.size(); i++) { // do something that does not modify vector size... } int size = vector.size(); for (int i = 0; i < size; i++) { // do something that does not
ArrayList上循环
for (int i = 0; i < vector.size(); i++) {
// do something that does not modify vector size...
}
int size = vector.size();
for (int i = 0; i < size; i++) {
// do something that does not modify vector size...
}
实际上效率稍微高一点。本书编写于2000年,作者使用的是Sun 1.2.2 JDK
这种情况对较新的JDK仍然适用吗?或者Java编译现在是否足够聪明,可以消除这些低效,尽管它们可能很小
编辑:我不担心代码中的这些微小优化;我只是对JDK的发展感到好奇。检查循环字节码:
12:iload\u 3
13:aload_2
14:invokeinterface#4,1//InterfaceMethod java/util/List.size:()I
19:if_icmpge 31
22:iinc 1,1
25:iinc 3,1
28:转到12
将其放入可变字节码中:
10:aload_2
11:invokeinterface#4,1//InterfaceMethod java/util/List.size:()I
16:istore_3
17:iconst_0
18:istore 4
20:iload 4
22:iload_3
23:if_icmpge 35
26:iinc 1,1
29:iinc 4,1
32:转到20
看起来它每次都在调用它,所以实际上把它放在一个变量中更快,我不担心它。请注意,我是字节码新手,可能完全错了。这是size()int ArrayList类的实现
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
在这种情况下,因为它存储在对象的属性中,所以它只是一个函数调用,并返回size的值(不计算它)。所以这里它只是防止一个函数调用。
如果size()方法每次调用时都在列表中迭代以计算大小,那么将大小存储在变量中肯定是有意义的。因为size()是一种方法,如果每次在循环中对其求值,则速度将慢于对其求值一次并将其存储在变量中。问题不在于它再次计算数组大小;相反,这是调用函数的开销。无论方法中包含什么,这都会影响性能(当然,长、慢、复杂的函数比简单的getter对性能的影响更大)
我很小心地说,每次都会计算“if”,因为编译器可能会决定内联函数调用,这将消除开销,循环也会同样快。这与for each与generic for循环的辩论相同。如果for-each函数调用没有内联,那么它将比没有函数调用的常规for循环慢
当然,在某些情况下,这会对性能产生很大影响,因此了解这些微妙之处是很好的。需要高吞吐量的实时信号处理算法是对不必要的开销敏感的程序的好例子。当然,这些通常不是用java编写的,但仍然是这样,但是了解这些东西是很好的
这种情况对较新的JDK仍然适用吗?或者Java编译现在是否足够聪明,可以消除这些低效,尽管它们可能很小
考虑到“Java编译器”javac
,没有任何变化,而且很可能永远不会。它的工作不是做任何优化因此查看生成的字节码是毫无意义的。
优化在运行时由编译器完成(Oracle热点)。它当然可以内联这样一个简单的方法,而且它很可能还可以在寄存器中缓存大小,从而消除内存访问。为此,它需要能够将所有内容内联到方法中,否则就不能保证vector.size
不会更改
PS:真正的性能问题可能是使用Vector
,这是一个多年来毫无意义的类。首选ArraysList
,除非您可以证明反复调用size()
是代码中的瓶颈,否则不要浪费时间担心这些微优化。在任何情况下,优化都在某些条件下是安全的(即,在循环中,没有其他线程可以改变<代码>向量的大小)。考虑使用<<代码>每个循环,而不必担心<代码>列表的内部结构。在java 8List中#forEach(…)
将进一步隐藏它。然而,“除非绝对必须在循环中做某事,否则不要在循环中做某事”的基本规则仍然适用。无论技术如何发展,在循环中进行较少的计算都是一件好事。字节码是100%不相关的,请参见。不。只要这有关系,方法调用就会内联。可能有这样的情况不会发生,我期待着看到一个真实的情况。