JIT放棄執行DoubleCalcTest
,因爲它沒有任何副作用(純計算)並且不使用結果。循環本身也可以被優化,因爲沒有效果。
與JIT去嘗試這一點,它需要大約8000毫秒:
java -Xint snippet.Snippet
在byteocde水平,沒有什麼是優化的。
javap -c snippet.Snippet
結果:
public class snippet.Snippet {
public snippet.Snippet();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #16 // Method java/lang/System.currentTimeMillis:()J
3: lstore_1
4: iconst_0
5: istore_3
6: goto 16
9: invokestatic #22 // Method DoubleCalcTest:()D
12: pop2
13: iinc 3, 1
16: iload_3
17: ldc #26 // int 100000000
19: if_icmplt 9
22: invokestatic #16 // Method java/lang/System.currentTimeMillis:()J
25: lstore_3
26: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
29: new #31 // class java/lang/StringBuilder
32: dup
33: ldc #33 // String That took
35: invokespecial #35 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
38: lload_3
39: lload_1
40: lsub
41: invokevirtual #38 // Method java/lang/StringBuilder.append:(J)Ljava/lang/StringBuilder;
44: ldc #42 // String milliseconds
46: invokevirtual #44 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: invokevirtual #47 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
52: invokevirtual #51 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
55: return
public static double DoubleCalcTest();
Code:
0: ldc2_w #64 // double 987.654321d
3: dstore_0
4: ldc2_w #66 // double 123.456789d
7: dstore_2
8: dload_0
9: dload_2
10: dadd
11: dstore_0
12: dload_0
13: dload_2
14: dsub
15: dstore_0
16: dload_0
17: dload_2
18: dmul
19: dstore_0
20: dload_0
21: dload_2
22: ddiv
23: dreturn
}
如果您嘗試使用導致DoubleCalc的()通過將其分配給一個變量,然後打印出來。
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
double res = 0;
for (int i = 0; i < 100000000; i++) {
res = DoubleCalcTest();
}
System.out.println(res);
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
}
這將需要相同的時間。爲什麼? JIT似乎足夠聰明地理解結果並不取決於迭代完成的時間。
但是,如果將其更改爲:
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
double res = 0;
for (int i = 0; i < 100000000; i++) {
res += DoubleCalcTest();
}
System.out.println(res);
long endTime = System.currentTimeMillis();
System.out.println("That took " + (endTime - startTime) + " milliseconds");
}
結果取決於迭代次數和JIT不進一步優化。在這種情況下,大約需要100毫秒。如果我爲2億更換1億,則需要兩倍的時間。
所以結論是,JIT在那裏停留。
注:
對於給定的C程序:
#include <stdio.h>
int main(int argc, char** argv) {
long x = 0;
int i;
for(i=0; i<1000000; i++) {
x+=i;
}
printf("%ld", x);
}
GCC能夠在編譯時完全優化的循環並計算x的值:
gcc -O2 -S main.c
主.s:
.file "main.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%ld"
.section .text.startup,"ax",@progbits
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB11:
.cfi_startproc
movabsq $499999500000, %rsi <---- See, this is the pre-computed result
movl $.LC0, %edi
xorl %eax, %eax
jmp printf
.cfi_endproc
.LFE11:
.size main, .-main
.ident "GCC: (GNU) 4.7.2 20121109 (Red Hat 4.7.2-8)"
.section .note.GNU-stack,"",@progbits
很酷,嘿?
優化了...? –
我一開始也這麼認爲,但它並不能解釋爲什麼需要3毫秒來運行100,000次。 – Xin
我測試了你的代碼,大約80毫秒。 - 我安裝了最新的java環境。 – shuangwhywhy