代码之家  ›  专栏  ›  技术社区  ›  Dan

基元数组的现代for循环

  •  12
  • Dan  · 技术社区  · 17 年前

    基元数组上的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
    

    所以现代循环实际上运行得更快,至少在我的MacOSXJVM15上是这样。

    5 回复  |  直到 11 年前
        1
  •  4
  •   erickson    17 年前

    您手写的“旧”窗体执行的指令更少,而且可能更快,尽管您必须在给定的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: }
    

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

    第1行可能看起来是不必要的,但是如果数组是在进入循环之前通过一个方法计算出来的,那么它就是缓存结果的样板。

    也就是说,我将使用新表单,除非您需要对索引变量做些什么。任何性能差异都可能在运行时被JIT编译器优化掉,新的形式更为清晰。如果您继续“手工”进行优化,可能会错过未来的优化。一般来说,一个好的编译器可以很好地优化“愚蠢”的代码,但会碰到“聪明”的代码。

        2
  •  5
  •   Bill K    17 年前

    我认为你不知道也不应该猜。这些天试图胜过编译器是徒劳的。

    曾经有人学会了“模式”来优化某些操作,但是在下一个版本的Java中,这些模式实际上是比较慢的。

    总是尽可能清楚地写出来,不要担心优化,直到你真正掌握了一些用户规范并且不能满足某些需求,甚至在测试前后要非常小心地运行,以确保你的“修复”实际上已经足够地改进了它,使需求通过。

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

    准时编译意味着它有时会胜过C,并且在某些情况下没有理由它不能胜过静态汇编语言(程序集不能预先确定调用不是必需的,Java有时可以做到这一点)。

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

        3
  •  2
  •   Toon Krijthe    17 年前

    为什么不自己测量呢?

    这听起来有点苛刻,但这种问题很容易让你自己验证。

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

        4
  •  1
  •   Paulo Guedes    17 年前

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

        5
  •  1
  •   Paulo Guedes    17 年前

    我对你的问题很好奇,即使在我之前的回答之后。所以我决定亲自检查一下。我写了这段代码(请忽略检查数字是否为素数的数学正确性;-):

    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),所以我认为我们可以得出这样的结论:两个循环具有相同的性能——运行时间的变化是由机器的其他应用程序(甚至操作系统)引起的。