简体   繁体   English

Java ArrayList for循环优化

[英]Java ArrayList for-loop optimization

In Dov Bulka's Java Performance and Scalability: Volume 1 , the author mentions that looping over an ArrayList with something such as 在多夫布尔卡的Java性能和可扩展性:第1卷 ,作者提到,遍历一个ArrayList的东西,如

for (int i = 0; i < vector.size(); i++) {
    // do something that does not modify vector size...
}

is actually a minor optimization issue because of the constant computation of vector.size() , thus implying that something such as 实际上,由于vector.size()的不断计算,这实际上是一个次要的优化问题,这意味着诸如

int size = vector.size();    
for (int i = 0; i < size; i++) {
    // do something that does not modify vector size...
}

is actually slightly more efficient. 实际上效率更高。 As the book was written in 2000, the author was using the Sun 1.2.2 JDK. 当这本书写于2000年时,作者使用的是Sun 1.2.2 JDK。

Does this condition still hold for newer JDKs? 对于新的JDK,此条件是否仍然成立? Or is Java compilation now smart enough to remove these inefficiencies, despite how minor they may be? 还是Java编译现在足够聪明,可以消除这些低效率的问题,尽管它们可能有多小?

EDIT: I don't worry about these tiny optimizations in my code; 编辑:我不用担心我的代码中的这些微小的优化; I was simply curious about the evolution of the JDK. 我只是对JDK的发展感到好奇。

Checking in a loop byte-code: 检入循环字节码:

12: iload_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: goto          12

Putting it in a variable byte-code: 将其放入可变字节码中:

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: goto          20

Seems like it's invoking it every time, so actually putting it in a variable is faster, I wouldn't worry about it tho. 似乎它每次都在调用它,因此实际上将其放入变量中的速度更快,因此我无需担心。 Be aware that I'm newbie in byte-code and I may be completely wrong. 请注意,我是字节码新手,我可能完全错了。

This is the implementation of size() int ArrayList class 这是size()int ArrayList类的实现

/**
 * Returns the number of elements in this list.
 *
 * @return the number of elements in this list
 */
public int size() {
    return size;
}

In this case since it is stored in object's property, so its just a function call and returns the value of size(does not calculate it).So here it is just about preventing one function call. 在这种情况下,因为它存储在对象的属性中,所以它只是一个函数调用并返回size的值(不计算它)。因此这里只是防止一个函数调用。

It definitely makes sense to store size in a variable, if the size() method iterates over the list to calculate size, everytime it is called. 如果将size()方法遍历列表以计算大小,则每次调用它时,将大小存储在变量中绝对是合理的。

Because size() is a method, if it is evaluated every time in the loop it will be slower than evaluating it once and storing it in a variable. 因为size()是一种方法,所以如果每次在循环中都对其求值,它将比一次对其进行评估并将其存储在变量中要慢。 The issue is not that it calculates the array size again; 问题不在于它会再次计算数组大小;而是 rather it is the overhead of calling a function at all. 而是完全是调用一个函数的开销。 This will hurt performance regardless of what is in the method (though of course a long, slow, complicated function will hurt it more than a simple getter). 无论方法是什么,这都会损害性能(尽管长,慢,复杂的功能会比简单的吸气剂损害更多)。

I was careful to say "if" it is evaluated every time because compilers may decide to inline the function call which would eliminate the overhead and the loop will be just as fast. 我谨慎地说“每次”都会被评估,因为编译器可能会决定内联函数调用,这将消除开销,并且循环将同样快。 This is the same as the for-each vs. generic for loop debate. 这与for-each与通用for循环辩论相同。 If the for-each function calls are not inlined, it will be slower than a general for loop with no function calls. 如果没有内联for-each函数调用,它将比没有函数调用的常规for循环慢。

There are definitely instances where this can make a big difference in performance so it is good to be aware of these subtleties. 在某些情况下,肯定会在性能上产生很大差异,因此请务必注意这些细微之处。 Real time signal processing algorithms that need to have high throughput are good examples of programs that could be sensitive to unnecessary overhead. 需要具有高吞吐量的实时信号处理算法是可能对不必要的开销敏感的程序的良好示例。 Granted, those are not usually written in java but still, but it is good to know about these things. 当然,这些通常不是用Java编写的,但仍然是用Java编写的,但是很高兴知道这些事情。

Does this condition still hold for newer JDKs? 对于新的JDK,此条件是否仍然成立? Or is Java compilation now smart enough to remove these inefficiencies, despite how minor they may be? 还是Java编译现在足够聪明,可以消除这些低效率的问题,尽管它们可能有多小?

Considering the "Java compiler" javac , nothing has changed and most probably never will. 考虑到“ Java编译器” javac ,没有任何改变,而且很可能永远不会改变。 It's not its job to do any optimizations. 进行任何优化不是它的工作。 So looking at the generated bytecode is pointless. 因此,查看生成的字节码是没有意义的。

Optimizations get done at runtime by the JIT compiler (Oracle Hotspot). JIT编译器(Oracle Hotspot)在运行时完成了优化。 It surely can inline such a trivial method and it most probably also can cache the size in a register so the memory access gets eliminated. 它肯定可以内联这种简单的方法,并且很可能还可以将大小缓存在寄存器中,从而消除了对内存的访问。 For this, it needs to be able to inline everything into the method - otherwise there'd be no guarantee that vector.size does not change. 为此,它需要能够将所有内容内联到该方法中-否则无法保证vector.size不会改变。

PS: The real performance problem may be the use of Vector , which is a pretty pointless class since years. PS:真正的性能问题可能是使用Vector ,这是多年来以来毫无意义的一类。 Prefer ArraysList . ArraysList

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM