原始数组上的for循环之间是否有任何性能差异?
假设:
double[] doubleArray = new double[300000];
for (double var: doubleArray)
someComplexCalculation(var);
或:
for ( int i = 0, y = doubleArray.length; i < y; i++)
someComplexCalculation(doubleArray[i]);
测试结果
我实际上是在描述它:
Total timeused for modern loop= 13269ms
Total timeused for old loop = 15370ms
所以现代循环实际上运行得更快,至少在我的Mac OSX JVM 1.5上。
答案 0 :(得分:5)
我的意见是你不知道也不应该猜。这些天试图超越编译器是徒劳的。
有些时候人们学习“模式”似乎可以优化某些操作,但在下一版本的Java中,这些模式实际上更慢。
始终尽可能清楚地写出来并且不要担心优化,直到你手中有一些用户规范并且无法满足某些要求,甚至在测试之前和之后都要非常小心地运行确保您的“修复”实际上已经足够改进,以使该要求通过。
编译器可以执行一些令人惊奇的事情,这些事情真的会让你大吃一惊,即使你做了一些迭代超过某个大范围的测试,如果你有一个较小的范围或改变循环中发生的事情,它可能会完全不同
及时编译意味着它偶尔可以胜过C,并且在某些情况下它没有理由不能胜过静态汇编语言(程序集无法事先确定不需要调用,Java有时可以做只是那个。
总结一下:你可以在代码中加入的最大值是把它写成可读的。
答案 1 :(得分:4)
您手写的“旧”表单执行的指令较少,并且可能更快,尽管您必须在给定的JIT编译器下对其进行概要分析才能确定。 “新”形式肯定不更快。
如果查看反汇编代码(由Sun的JDK 1.5编译),您会看到“new”表单等同于以下代码:
1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3: double var = tmp[i];
4: someComplexCalculation(var);
5: }
因此,您可以看到使用了更多局部变量。第1行的doubleArray
到tmp
的分配是“额外的”,但它不会在循环中发生,并且可能无法测量。第3行的var
分配也是额外的。如果性能存在差异,这将是负责任的。
第1行可能看起来没必要,但是如果在进入循环之前通过方法计算数组,则缓存结果的是样板。
那就是说,我会使用新表单,除非你需要对索引变量做一些事情。 JIT编译器在运行时可能会优化任何性能差异,并且新表单更加清晰。如果您继续“手动”执行此操作,您可能会错过未来的优化。一般来说,一个好的编译器可以很好地优化“愚蠢”的代码,但偶然发现“智能”代码。
答案 2 :(得分:2)
为什么不亲自测量?
这听起来有点刺耳,但这类问题很容易验证自己。
只需创建数组并执行1000次或更多次循环,然后测量时间。重复几次以消除毛刺。
答案 3 :(得分:1)
没有区别。 Java会将增强型转换为正常的for循环。增强版只是一种“语法糖”。两个循环生成的字节码相同。
答案 4 :(得分:1)
即使在我之前的回答之后,我对你的问题也很好奇。所以我决定自己检查一下。我写了这段小代码(请忽略关于检查数字是否为素数的数学正确性; - )):
public class TestEnhancedFor {
public static void main(String args[]){
new TestEnhancedFor();
}
public TestEnhancedFor(){
int numberOfItems = 100000;
double[] items = getArrayOfItems(numberOfItems);
int repetitions = 0;
long start, end;
do {
start = System.currentTimeMillis();
doNormalFor(items);
end = System.currentTimeMillis();
System.out.printf("Normal For. Repetition %d: %d\n",
repetitions, end-start);
start = System.currentTimeMillis();
doEnhancedFor(items);
end = System.currentTimeMillis();
System.out.printf("Enhanced For. Repetition %d: %d\n\n",
repetitions, end-start);
} while (++repetitions < 5);
}
private double[] getArrayOfItems(int numberOfItems){
double[] items = new double[numberOfItems];
for (int i=0; i < numberOfItems; i++)
items[i] = i;
return items;
}
private void doSomeComplexCalculation(double item){
// check if item is prime number
for (int i = 3; i < item / 2; i+=2){
if ((item / i) == (int) (item / i)) break;
}
}
private void doNormalFor(double[] items){
for (int i = 0; i < items.length; i++)
doSomeComplexCalculation(items[i]);
}
private void doEnhancedFor(double[] items){
for (double item : items)
doSomeComplexCalculation(item);
}
}
运行应用程序给了我以下结果:
正常。重复0:5594 增强功能。重复0:5594
正常。重复1:5531 增强功能。重复1:5547
正常。重复2:5532 增强功能。重复2:5578
正常。重复3:5531 增强功能。重复3:5531
正常。重复4:5547 增强功能。重复4:5532
正如我们所看到的,结果之间的差异非常小,有时正常循环运行得更快,有时增强循环更快。由于我的电脑中还有其他应用程序打开,我觉得很正常。此外,只有第一次执行比其他执行慢 - 我相信这与JIT优化有关。
正常循环的平均时间(不包括第一次重复)为5535,25ms,增强循环的平均时间为5547ms。但是我们可以看到两个循环的最佳运行时间是相同的(5531ms),所以我认为我们可以得出两个循环具有相同性能的结论 - 并且经过的时间变化是由于其他应用程序(甚至机器的操作系统。