Java 调用array.length的成本是多少
在应用程序中将for循环更新为for each循环时,我遇到了很多这样的“模式”:Java 调用array.length的成本是多少,java,arrays,premature-optimization,Java,Arrays,Premature Optimization,在应用程序中将for循环更新为for each循环时,我遇到了很多这样的“模式”: for(int i=0,n=a.length;i
for(int i=0,n=a.length;i
而不是
for (int i = 0; i < a.length; i++) {
...
}
for(int i=0;i
我可以看出,由于不需要在每个循环中调用size()方法,所以可以提高集合的性能。但是使用数组
于是问题出现了:
array.length
是否比常规变量更昂贵?将其存储在变量中可能会稍微快一点。但如果一个剖析者指出这是一个问题,我会非常惊讶
在字节码级别,使用
arraylength
字节码获取数组的长度。我不知道它是否比一个iload
字节码慢,但应该没有足够的差异来注意。不,调用array.length
是O(1)
或常量时间操作
由于.length
是数组
的公共
最终
成员,因此它的访问速度并不比局部变量慢。(这与调用像size()
这样的方法非常不同)
现代JIT编译器可能会立即优化对.length
的调用
您可以通过查看OpenJDK中JIT编译器的源代码,或者让JVM转储JIT编译的本机代码并检查代码来确认这一点
请注意,在某些情况下,JIT编译器可能无法做到这一点;e、 g
我怀疑是否有任何显著的差异,即使有,我敢打赌它可能在编译过程中被优化了。当你试图对这样的事情进行微优化时,你是在浪费时间。首先使代码可读且正确,然后如果出现性能问题,请使用探查器,然后担心选择更好的数据结构/算法(如果合适),然后担心优化探查器突出显示的部分。数组的长度存储为数组的成员变量(与元素不同)在java中,获取长度是一个固定的时间操作,就像从一个普通类中读取一个成员变量一样。许多像C和C++这样的老语言不把长度存储为数组的一部分,所以你想在循环开始之前存储长度。你不必在java中这样做。 < p>这个答案是从C的观点来看的,但是我认为Java也是如此 C#中的成语
for (int i = 0; i < a.length; i++) { ...}
for(inti=0;i
被识别为在数组上迭代,因此在循环中访问数组时避免了边界检查,而不是每次访问数组时
这可能通过以下代码识别,也可能无法识别:
for (int i = 0, n = a.length; i < n; i++) { ...}
for(inti=0,n=a.length;i
或
n=a.长度;
对于(int i=0;i
这种优化在多大程度上是由编译器执行的,而不是由抖动执行的,我不知道,特别是如果它是由抖动执行的,我希望所有3个都会生成相同的本机代码
然而,第一种形式也可以说更容易被人们阅读,所以我想说还是这样吧。我在午餐时有点时间:
public static void main(String[] args) {
final int[] a = new int[250000000];
long t;
for (int j = 0; j < 10; j++) {
t = System.currentTimeMillis();
for (int i = 0, n = a.length; i < n; i++) { int x = a[i]; }
System.out.println("n = a.length: " + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
for (int i = 0; i < a.length; i++) { int x = a[i]; }
System.out.println("i < a.length: " + (System.currentTimeMillis() - t));
}
}
publicstaticvoidmain(字符串[]args){
最终整数[]a=新整数[250000000];
长t;
对于(int j=0;j<10;j++){
t=System.currentTimeMillis();
对于(inti=0,n=a.length;i
结果是:
n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n=a.长度:672
长度:516
n=a。长度:640
长度:516
n=a。长度:656
长度:516
n=a。长度:656
长度:516
n=a。长度:640
长度:532
n=a。长度:640
长度:531
n=a。长度:641
长度:516
n=a。长度:656
长度:531
n=a。长度:656
长度:516
n=a。长度:656
长度:516
注:
n=a.length
显示比i
快大约一半,可能是由于垃圾收集(?)250000000
大得多,因为我在270000000处获得了OutOfMemoryError
关键是,这是每个人都在做的事情,你必须在内存不足的情况下运行Java,但你仍然看不到这两种方法在速度上的显著差异。把你的开发时间花在真正重要的事情上。Array.length是一个常量,JIT编译器应该在这两种情况下都能看穿它在这两种情况下,我们都希望生成的机器代码是相同的。至少对于服务器编译器是一样的。在这种情况下,为什么不做一个反向循环:
for (int i = a.length - 1; i >= 0; --i) {
...
}
这里有两个微优化:
- 环路反转
- 后缀减量
.length
的调用。”[引文需要]+1。除非你在循环中做一些非常琐碎的事情,否则你在循环中所做的一切都是错误的
n = a.length: 672
i < a.length: 516
n = a.length: 640
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
n = a.length: 640
i < a.length: 532
n = a.length: 640
i < a.length: 531
n = a.length: 641
i < a.length: 516
n = a.length: 656
i < a.length: 531
n = a.length: 656
i < a.length: 516
n = a.length: 656
i < a.length: 516
for (int i = a.length - 1; i >= 0; --i) {
...
}