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 毫秒。如果我将 100000000 更改为 200000000,则需要两倍的时间。
所以结论是 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
电源:
.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
很酷吧?