2013-04-25 8 views
10

Javaのfor、while、do-whileループの効率を比較しています。私は理解を助ける必要があるいくつかの奇妙な結果に出くわしました。Javaループ効率

public class Loops { 
    public static void main(String[] args) { 
     int L = 100000; // number of iterations per loop 
     // for loop 
     double start = System.currentTimeMillis(); 
     long s1 = 0; 
     for (int i=0; i < L; i++) { 
      for (int j = 0; j < L; j++) { 
       s1 += 1; 
      } 
     } 
     double end = System.currentTimeMillis(); 
     String result1 = String.format("for loop: %.5f", (end-start)/1000); 
     System.out.println(s1); 
     System.out.println(result1); 

     // do-while loop 
     double start1 = System.currentTimeMillis(); 
     int i = 0; 
     long s2 = 0; 
     do { 
      i++; 
      int j = 0; 
      do { 
       s2 += 1; 
       j++; 
      } while (j < L); 
     } while (i < L); 
     double end1 = System.currentTimeMillis(); 
     String result2 = String.format("do-while: %.5f", (end1-start1)/1000); 
     System.out.println(s2); 
     System.out.println(result2); 

     // while loop 
     double start2 = System.currentTimeMillis(); 
     i = 0; 
     long s3 = 0; 
     while (i < L) { 
      i++; 
      int j = 0; 
      while (j < L) { 
       s3 += 1; 
       j++; 
      } 
     } 
     double end2 = System.currentTimeMillis(); 
     String result3 = String.format("while: %.5f", (end2-start2)/1000); 
     System.out.println(s3); 
     System.out.println(result3); 
    } 
} 

すべてのループの合計カウンタは合計100億です。結果は私を当惑:

forループ

:6.48300

行う-中:0.41200

中:9.71500

なぜDO-whileループそんなに速く?このパフォーマンスギャップは、Lへの変更と並行してスケールされます。私はこれらのループを独立して実行し、同じことを実行します。

+1

番号を再現できません。どちらのwhileループもforループを使って同じスピードで走りますが、少し遅くなります。 – Mysticial

+5

いずれにしても、コンパイラまたはJITが内部ループを完全に削除できる可能性があるため、これは特に大きなベンチマークではありません。 – Mysticial

+1

そうでなければなりません。do-whileループのためだけに実行される何らかの最適化です。それでも、私はこのメカニズムについてもっと知りたいです。 – JohnF

答えて

18

あなたが提供したコードを実行しましたが、これらのパフォーマンスの違いを見ると驚いています。好奇心でリード私は調査を始め、実際にこれらのループにもかかわらず、同じことをしているようだが、それらの間にいくつかの重要な違いがあることが分かった。これらのループの最初の実行後に

私の結果は以下の通りであった。

for loop: 1.43100 
do-while: 0.51300 
while: 1.54500 

しかし、私はこれらの3つのループを実行したときに少なくとも10回は、これらのループのそれぞれの性能はほとんど同じでした。

for loop: 0.43200 
do-while: 0.46100 
while: 0.42900 

JITは、時間の経過とともに、これらのループを最適化することが可能であるが、異なる初期の性能を有するようにこれらのループを引き起こすいくつかの相違が存在しなければなりません。実際には、実際に2つの相違点がある:

  • do-whileループforより少ない比較を行っているとwhileは簡単のため

ループは、L = 1

long s1 = 0; 
for (int i=0; i < L; i++) { 
    for (int j = 0; j < L; j++) { 
     s1 += 1; 

外側ループを想定します:0 内側ループ:0 内部ループ:1 アウターループ:総

int i = 0; 
long s2 = 0; 
do { 
    i++; 
    int j = 0; 
    do { 
     s2 += 1; 
     j++; 
    } while (j < L); 
} while (i < L); 

内側ループにおける1つの

4比較:1 アウターループ:1

合計2件の比較

  • 異なる生成したバイトコード

はさらなる調査のために、私はそれが動作する方法に影響を与えない、少し自分のクラスを変更しました。

public class Loops { 
    final static int L = 100000; // number of iterations per loop 

    public static void main(String[] args) { 
     int round = 10; 
     while (round-- > 0) { 
      forLoop(); 
      doWhileLoop(); 
      whileLoop(); 
     } 
    } 

    private static long whileLoop() { 
     int i = 0; 
     long s3 = 0; 
     while (i++ < L) { 
      int j = 0; 
      while (j++ < L) { 
       s3 += 1; 
      } 
     } 
     return s3; 
    } 

    private static long doWhileLoop() { 
     int i = 0; 
     long s2 = 0; 
     do { 
      int j = 0; 
      do { 
       s2 += 1; 
      } while (++j < L); 
     } while (++i < L); 
     return s2; 
    } 

    private static long forLoop() { 
     long s1 = 0; 
     for (int i = 0; i < L; i++) { 
      for (int j = 0; j < L; j++) { 
       s1 += 1; 
      } 
     } 
     return s1; 
    } 
} 

次に、コンパイルしてjavap -c -s -private -l Loopを呼び出して、バイトコードを取得します。

最初にdoWhileLoopのバイトコード。今

0: iconst_0  // push the int value 0 onto the stack 
    1: istore_1  // store int value into variable 1 (i) 
    2: lconst_0  // push the long 0 onto the stack 
    3: lstore_2  // store a long value in a local variable 2 (s2) 
    4: iconst_0  // push the int value 0 onto the stack 
    5: istore 4 // store int value into variable 4 (j) 
    7: lload_2  // load a long value from a local variable 2 (i) 
    8: lconst_1  // push the long 1 onto the stack 
    9: ladd  // add two longs 
    10: lstore_2  // store a long value in a local variable 2 (i) 
    11: iinc 4, 1 // increment local variable 4 (j) by signed byte 1 
    14: iload 4 // load an int value from a local variable 4 (j) 
    16: iload_0  // load an int value from a local variable 0 (L) 
    17: if_icmplt 7 // if value1 is less than value2, branch to instruction at 7 
    20: iinc 1, 1 // increment local variable 1 (i) by signed byte 1 
    23: iload_1  // load an int value from a local variable 1 (i) 
    24: iload_0  // load an int value from a local variable 0 (L) 
    25: if_icmplt 4 // if value1 is less than value2, branch to instruction at 4 
    28: lload_2  // load a long value from a local variable 2 (s2) 
    29: lreturn  // return a long value 

while文のバイトコード:

0: iconst_0  // push int value 0 onto the stack 
    1: istore_1  // store int value into variable 1 (i) 
    2: lconst_0  // push the long 0 onto the stack 
    3: lstore_2  // store a long value in a local variable 2 (s3) 
    4: goto  26 
    7: iconst_0  // push the int value 0 onto the stack 
    8: istore 4 // store int value into variable 4 (j) 
    10: goto  17 
    13: lload_2  // load a long value from a local variable 2 (s3) 
    14: lconst_1  // push the long 1 onto the stack 
    15: ladd  // add two longs 
    16: lstore_2  // store a long value in a local variable 2 (s3) 
    17: iload 4 // load an int value from a local variable 4 (j) 
    19: iinc 4, 1 // increment local variable 4 (j) by signed byte 1 
    22: iload_0  // load an int value from a local variable 0 (L) 
    23: if_icmplt 13 // if value1 is less than value2, branch to instruction at 13 
    26: iload_1  // load an int value from a local variable 1 (i) 
    27: iinc 1, 1 // increment local variable 1 by signed byte 1 
    30: iload_0  // load an int value from a local variable 0 (L) 
    31: if_icmplt 7 // if value1 is less than value2, branch to instruction at 7 
    34: lload_2  // load a long value from a local variable 2 (s3) 
    35: lreturn  // return a long value 

が、私は、各命令が‪Java bytecode instruction listingsに基づいん何説明するコメントを追加している出力を読みやすくするため。

これらの2つのバイトコードの間には大きな違いがあることがわかります。 whileループ(forループでも同じです)には、バイトコードの最後にif文(if_icmplt命令)が定義されています。つまり、最初のループの終了条件をチェックするには、26行目への移動を呼び出す必要があります。同様に、2番目のループの17行目に移動します。

上記のバイトコードは、Mac OS X上のjavac 1.6.0_45を使用して生成された

概要と思い

比較の異なる量を加えながら、内とループバイトコードのためのgoto命令の有無これらのループ間のパフォーマンスの差異に責任を負います。