38

Javaでは、通常、次のようなforループを作成します。

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

しかし最近、同僚がそれを次のように入力しました。

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

彼は後者の方が速いだろうと言った。本当?

4

11 に答える 11

63

いいえ、それは真実ではありません。多数の反復の各ループのタイミングを調整することでパフォーマンスを測定できますが、それらは同じになると確信しています。

神話はCから来たもので、前者はiをインクリメントしてから返すことで実装できるため、++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

これ以上速くなることはありません。コンパイラーとJITを備えたJVMは、そのような重要でない違いのミンスミートを作ります。

必要に応じて、通常のループ最適化手法を使用して、展開などの速度の利点を得ることができます。

于 2011-01-28T18:32:28.323 に答える
1

いいえ、まったく違いはありません。

これはC++からのものですが、この場合でもまったく違いはありません。違いがあるのは、iがオブジェクトであるところです。i ++はアイテムの元の変更されていない値を返す必要があるため、オブジェクトの追加コピーを作成する必要がありますが、++ iは変更されたオブジェクトを返すことができるため、コピーを保存します。

ユーザー定義オブジェクトを使用するC++では、コピーのコストがかなり高くなる可能性があるため、覚えておく価値があります。そして、このため、とにかく同じくらい良いので、人々はint変数にもそれを使用する傾向があります...

于 2011-01-31T13:22:34.170 に答える
1

「javap-cYourClassName」で逆コンパイルし、結果を確認して決定します。このようにして、コンパイラが実際に何をしているのかを確認できます。あなたが思っていることではありません。このようにして、一方の方法がもう一方の方法よりも速い理由もわかります。

于 2013-04-05T14:47:42.327 に答える
0

Javaでは違いはありません。increment式の結果が直接使用されていないため、最近のコンパイラ*はどちらの場合も同じバイトコード()を生成する必要があります。 3番目のオプションがありますが、それでも同じバイトコードです*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 に答える