在Java中的for循环中,++我真的比i ++快吗?

时间:2011-01-28 18:29:44

标签: java loops performance premature-optimization

在java中,我通常会像以下一样进行for循环:

for (int i = 0; i < max; i++) {
   something
}

但是最近有一位同事打字了:

for (int i = 0; i < max; ++i) {
   something
}

他说后者会更快。这是真的吗?

11 个答案:

答案 0 :(得分:56)

不,这不是真的。您可以通过为每个循环计时大量迭代来测量性能,但我相当肯定它们将是相同的。

神话来自C,其中++i被认为比i++更快,因为前者可以通过增加然后返回它来实现。后者可以通过将i的值复制到临时变量,递增i,然后返回临时变量来实现。第一个版本不需要制作临时副本,因此很多人认为它更快。但是,如果表达式用作语句,现代C编译器可以优化临时副本,以便在实践中没有区别。

答案 1 :(得分:36)

这个问题需要一些Java字节码。请考虑以下代码:

public class PostPre {
    public static void main(String args[]) {
        int n = 5;
        loop1(n);
        loop2(n);
    }

    public static void loop1(int n) {
        for (int i = 0; i < n; i++) {}
    }

    public static void loop2(int n) {
        for (int i = 0; i < n; ++i) {}
    }
}

现在编译并反汇编:

$ javac PostPre.java; javap -c PostPre.class 
Compiled from "PostPre.java"
public class PostPre {
  public PostPre();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5      
       1: istore_1      
       2: iload_1       
       3: invokestatic  #2                  // Method loop1:(I)V
       6: iload_1       
       7: invokestatic  #3                  // Method loop2:(I)V
      10: return        

  public static void loop1(int);
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: iload_0       
       4: if_icmpge     13
       7: iinc          1, 1
      10: goto          2
      13: return        

  public static void loop2(int);
    Code:
       0: iconst_0      
       1: istore_1      
       2: iload_1       
       3: iload_0       
       4: if_icmpge     13
       7: iinc          1, 1
      10: goto          2
      13: return        
}

loop1()loop2()具有相同的字节代码。

答案 2 :(得分:9)

对于任何合理的优化器,它们都是完全相同的。如果您不确定,请查看输出字节码或对其进行分析。

答案 3 :(得分:5)

即使它是,我非常怀疑,你的同事应该花更多的时间来学习如何优化循环表达式。

答案 4 :(得分:2)

在您的环境中尝试此操作

public class IsOptmized {
    public static void main(String[] args) {

        long foo; //make sure the value of i is used inside the loop
        long now = 0; 
        long prefix = 0;
        long postfix = 0;

        for (;;) {
            foo = 0;
            now = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; i++) {
                foo += i;
            }
            postfix = System.currentTimeMillis() - now;

            foo = 0;
            now = System.currentTimeMillis();
            for (int i = 0; i < 1000000000; ++i) {
                foo += i;
            }
            prefix = System.currentTimeMillis() - now;

            System.out.println("i++ " + postfix + " ++i " + prefix + " foo " + foo);
        }
    }
}

我给了我

i++ 1690 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1611 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1600 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1692 ++i 1610 foo 499999999500000000
i++ 1701 ++i 1610 foo 499999999500000000
i++ 1691 ++i 1610 foo 499999999500000000

所以,即使不是那么多,我也认为存在差异

答案 5 :(得分:1)

它不会更快。带有JIT的编译器和JVM将使这些微不足道的差异变得很小。

您可以使用常用的循环优化技术来获得速度优势,例如展开(如果适用)。

答案 6 :(得分:1)

不会有任何差别。

这来自C ++,但即便如此,在这种情况下也没有任何区别。 哪里有区别就是我是一个对象。 i ++必须制作对象的附加副本,因为它必须返回项目的原始未更改值,而++我可以返回更改的对象,以便保存副本。

在使用用户定义对象的c ++中,副本的成本可能很高,所以它绝对值得记住。因此,人们倾向于将它用于int变量,因为它总是同样好......

答案 7 :(得分:1)

使用“javap -c YourClassName”进行反编译并查看结果并从中做出决定。通过这种方式,您可以看到编译器在每种情况下实际执行的操作,而不是您认为的操作。这样你也可以看到为什么一种方式比另一种更快。

答案 8 :(得分:0)

在Java中应该没有区别 - 任何现代编译器 * 都应该在两种情况下生成相同的字节代码(只是iinc),因为增量表达式的结果不是直接使用 还有第三个选项,仍然是相同的字节代码 *

for (int i = 0; i < max; i += 1) {
   something
}
用Eclipse的编译器测试

*

答案 9 :(得分:0)

在Java中没有这样的差异。 Java机器编写代码,无论你编写++ i还是i ++,它都会被转换为字节代码以完全相同的指令集。

但是在C / C ++中存在巨大的差异,如果你没有使用任何优化标志,那么你的循环最多可以慢3次。

使用像-O / -O3这样的优化标志将迫使编译器使输出组装代码更简单(在大多数情况下),因此更快(在大多数情况下)。

答案 10 :(得分:-2)

即使在HotSpot时代,人们会更快,无人问津。 JIT做的第一件事是删除javac所做的所有优化。在那之后,一切都留给JIT来使它快速。