38

在java中,我通常会做一个如下的for循环:

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

但最近一位同事这样打字:

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

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

4

11 回答 11

63

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

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

于 2011-01-28T18:31:49.570 回答
45

这个问题需要一些 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()具有相同的字节码。

于 2013-09-30T18:08:35.780 回答
9

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

于 2011-01-28T18:31:49.963 回答
6

即使是这样,我非常怀疑,你的同事真的应该有更好的东西来花时间学习,而不是如何优化循环表达式。

于 2011-01-28T18:31:23.727 回答
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

所以即使不是那么多,我认为还是有区别的

于 2015-02-23T14:47:08.833 回答
1

不,根本没有区别。

这来自 C++,但即使在这种情况下也不会有任何区别。不同之处在于 i 是一个对象。i++ 必须制作对象的附加副本,因为它必须返回项目的原始未更改值,而 ++i 可以返回更改的对象,因此保存副本。

在具有用户定义对象的 c++ 中,副本的成本可能很高,因此绝对值得记住。正因为如此,人们也倾向于将它用于 int 变量,因为无论如何它都一样好......

于 2011-01-31T13:22:34.170 回答
1

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

于 2013-04-05T14:47:42.327 回答
1

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

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

于 2011-01-28T18:32:28.323 回答
0

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

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

* 用 Eclipse 的编译器测试

于 2011-01-28T19:58:04.293 回答
0

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

但是在 C/C++ 中存在巨大差异,如果您不使用任何优化标志,那么您的循环可能会慢 3 倍。

使用诸如 -O/-O3 之类的优化标志将强制编译器使输出汇编代码更简单(在大多数情况下),因此更快(在大多数情况下)。

于 2015-08-10T09:11:46.640 回答
-2

即使它会更快,在 HotSpot 时代也没有人关心。JIT 做的第一件事是删除javac 所做的所有优化。之后,一切都留给 JIT 以使其快速运行。

于 2011-01-28T18:33:18.557 回答