The JIT discards the execution of DoubleCalcTest
since it does not have any side-effect (pure calculation) and the result is not used. The loop itself can also be optimized out since there is no effect.
Try this with JIT off and it will take around 8000 ms:
java -Xint snippet.Snippet
At the byteocde level, nothing is optimized out.
javap -c snippet.Snippet
result:
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
}
If you try to use to result of DoubleCalc() by assigning it to a variable and print it afterward.
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");
}
It will take the same time. Why? The JIT seems to be smart enough to understand that the result does not depend on the number of time the iteration is done.
However, if you change this to:
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");
}
The result depends on the number of iteration and the JIT does not optimize further. In this case, it takes about 100 ms. If I change 100000000 for 200000000, it takes twice as much time.
So the conclusion is that the JIT stops there.
NOTE:
For the give C program:
#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 is able to optimized the loop entirely and compute the value of x at compile-time:
gcc -O2 -S main.c
main.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
Pretty cool, heh?