13

原始数组上的 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 上是这样。

4

5 回答 5

5

我的观点是你不知道也不应该猜测。这些天试图超越编译器是徒劳的。

有时人们学习“模式”似乎可以优化某些操作,但在 Java 的下一个版本中,这些模式实际上更慢。

始终尽可能清楚地编写它,并且不要担心优化,直到您确实掌握了一些用户规范并且未能满足某些要求,即使如此,在测试之前和之后也要非常小心地运行以确保您的“修复”实际上对其进行了足够的改进以使该要求通过。

编译器可以做一些令人惊叹的事情,这些事情真的会让你大吃一惊,即使你做了一些迭代某个大范围的测试,如果你有一个更小的范围或改变循环内发生的事情,它的执行可能会完全不同。

即时编译意味着它有时可以胜过 C,并且在某些情况下它没有理由不能胜过静态汇编语言(汇编无法事先确定不需要调用,Java 有时可以做到这一点。

总而言之:您可以在代码中投入的最大价值就是将其编写为可读性。

于 2008-11-05T21:05:34.223 回答
4

您手写的“旧”表单执行的指令更少,并且可能更快,尽管您必须在给定的 JIT 编译器下对其进行分析才能确定。“新”形式肯定不会更快。

如果您查看反汇编代码(由 Sun 的 JDK 1.5 编译),您会看到“新”形式等价于以下代码:

1: double[] tmp = doubleArray;
2: for (int i = 0, y = tmp.length; i < y; i++) {
3:   double var = tmp[i];
4:   someComplexCalculation(var);
5: }

因此,您可以看到使用了更多的局部变量。doubleArray第 1 行对to的分配tmp是“额外的”,但它不会出现在循环中,并且可能无法测量。第 3 行的分配var也是额外的。如果在性能上有差异,这将是负责的。

第 1 行似乎没有必要,但如果数组是在进入循环之前通过方法计算的,则缓存结果是样板文件。

也就是说,我会使用新形式,除非你需要对索引变量做一些事情。JIT 编译器可能会在运行时优化掉任何性能差异,并且新形式更加清晰。如果您继续“手动”执行此操作,您可能会错过未来的优化。一般来说,一个好的编译器可以很好地优化“愚蠢”的代码,但会偶然发现“聪明”的代码。

于 2008-11-05T21:17:25.620 回答
2

为什么不自己测量呢?

这听起来有点刺耳,但是这类问题很容易验证自己。

只需创建数组并执行每个循环 1000 次或更多次,然后测量时间量。重复几次以消除故障。

于 2008-11-05T20:39:07.383 回答
2

没有区别。Java 会将增强的 for 转换为普通的 for 循环。增强的 for 只是一个“语法糖”。两个循环生成的字节码是相同的。

于 2008-11-05T20:45:17.370 回答
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,25 毫秒,增强循环为 5547 毫秒。但是我们可以看到两个循环的最佳运行时间是相同的(5531ms),所以我认为我们可以得出结论,两个循环具有相同的性能——并且经过时间的变化是由于其他应用程序(甚至机器的操作系统)。

于 2008-11-06T16:20:37.357 回答