あなたが提供したコードを実行しましたが、これらのパフォーマンスの違いを見ると驚いています。好奇心でリード私は調査を始め、実際にこれらのループにもかかわらず、同じことをしているようだが、それらの間にいくつかの重要な違いがあることが分かった。これらのループの最初の実行後に
私の結果は以下の通りであった。
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命令の有無これらのループ間のパフォーマンスの差異に責任を負います。
番号を再現できません。どちらのwhileループもforループを使って同じスピードで走りますが、少し遅くなります。 – Mysticial
いずれにしても、コンパイラまたはJITが内部ループを完全に削除できる可能性があるため、これは特に大きなベンチマークではありません。 – Mysticial
そうでなければなりません。do-whileループのためだけに実行される何らかの最適化です。それでも、私はこのメカニズムについてもっと知りたいです。 – JohnF