So I wanted to benchmark some basic java functionality to add some imformation to this question: What is the gain from declaring a method as static.
I know writing b
Preamble: Writing microbenchmarks manually is almost always doomed to a failure.
There are frameworks that have already solved the common benchmarking problems.
JIT compilation unit is a method. Incorporating several benchmarks into a single method leads to unpredictable results.
JIT heavily relies on the execution profile, i.e. the run-time statistics. If a method runs the first scenario for a long time, JIT will optimize the generated code for it. When the method suddenly switches to another scenario, do not expect it to run at the same speed.
JIT may skip optimizing the code that is not executed. It will leave an uncommon trap for this code. If the trap is ever hit, JVM will deoptimize the compiled method, switch to interpreter and after that recompile the code with the new knowledge. E.g. when your method run is compiled for the first time inside the first hot loop, JIT does not know about System.out.println yet. As soon as the execution reaches println, the earlier compiled code is likely to get deoptimized.
The bigger is method - the harder is to optimize it for JIT compiler. E.g. it may appear that there is not enough spare registers to hold all local variables. That's what happen in your case.
To sum it up, your benchmark seem to pass through the following scenario:
addStatic) triggers the compilation of run method. The execution profile does not know anything except addStatic method.System.out.println triggers the deoptimization and after that the second hot loop (addDynamic) leads to recompilation of run method.addDynamic, so JIT optimizes the second loop, and the first loop appears to have extra register spills:Optimized loop:
0x0000000002d01054: add %rbx,%r14
0x0000000002d01057: add $0x1,%rbx ;*ladd
; - TestPerformanceOfStaticVsDynamicCalls::addDynamic@2
; - TestPerformanceOfStaticVsDynamicCalls::run@105
0x0000000002d0105b: add $0x1,%r14 ; OopMap{rbp=Oop off=127}
;*goto
; - TestPerformanceOfStaticVsDynamicCalls::run@116
0x0000000002d0105f: test %eax,-0x1c91065(%rip) # 0x0000000001070000
;*lload
; - TestPerformanceOfStaticVsDynamicCalls::run@92
; {poll}
0x0000000002d01065: cmp $0x3b9aca00,%rbx
0x0000000002d0106c: jl 0x0000000002d01054
Loop with an extra register spill:
0x0000000002d011d0: mov 0x28(%rsp),%r11 <---- the problem is here
0x0000000002d011d5: add %r10,%r11
0x0000000002d011d8: add $0x1,%r10
0x0000000002d011dc: add $0x1,%r11
0x0000000002d011e0: mov %r11,0x28(%rsp) ;*ladd
; - TestPerformanceOfStaticVsDynamicCalls::addStatic@2
; - TestPerformanceOfStaticVsDynamicCalls::run@33
0x0000000002d011e5: mov 0x28(%rsp),%r11 <---- the problem is here
0x0000000002d011ea: add $0x1,%r11 ; OopMap{[32]=Oop off=526}
;*goto
; - TestPerformanceOfStaticVsDynamicCalls::run@44
0x0000000002d011ee: test %eax,-0x1c911f4(%rip) # 0x0000000001070000
;*goto
; - TestPerformanceOfStaticVsDynamicCalls::run@44
; {poll}
0x0000000002d011f4: cmp $0x3b9aca00,%r10
0x0000000002d011fb: jl 0x0000000002d011d0 ;*ifge
; - TestPerformanceOfStaticVsDynamicCalls::run@25
P.S. The following JVM options are useful to analyze the JIT compilation:
-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintAssembly -XX:CompileOnly=TestPerformanceOfStaticVsDynamicCalls