序言:手动编写微基准几乎注定要失败。
有些框架已经解决了常见的基准测试问题。
JIT编译单元是一种方法。将多个基准测试整合到一个方法中会导致无法预测的结果。
JIT严重依赖于执行配置文件,即运行时统计信息。如果某个方法长时间运行第一个方案,JIT将为其优化生成的代码。当该方法突然切换到另一个方案时,不要指望它以相同的速度运行。
JIT可能会跳过优化未执行的代码。它将为该代码留下一个不常见的陷阱。如果遇到陷阱,JVM将取消优化已编译的方法,切换到解释器,然后使用新知识重新编译代码。例如,当您的方法
run
在第一个热循环中第一次编译时,JIT尚不知道System.out.println
。一旦执行完成println
,较早的编译代码很可能会被优化。方法越大,为JIT编译器进行优化就越难。例如,似乎没有足够的备用寄存器来容纳所有局部变量。这就是您的情况。
综上所述,您的基准测试似乎通过了以下情形:
- 第一个热循环(
addStatic
)触发run
方法的编译。执行概要除了addStatic
方法外什么都不知道。 System.out.println
触发去优化,然后第二个热循环(addDynamic
)导致run
方法重新编译。- 现在执行配置文件仅包含有关的信息
addDynamic
,因此JIT优化了第二个循环,而第一个循环似乎有额外的寄存器溢出:
优化循环:
0x0000000002d01054: add %rbx,%r140x0000000002d01057: add $0x1,%rbx ;*ladd ; - TestPerformanceOfStaticVsDynamicCalls::addDynamic@2 ; - TestPerformanceOfStaticVsDynamicCalls::run@1050x0000000002d0105b: add $0x1,%r14 ; OopMap{rbp=Oop off=127} ;*goto ; - TestPerformanceOfStaticVsDynamicCalls::run@1160x0000000002d0105f: test %eax,-0x1c91065(%rip) # 0x0000000001070000 ;*lload ; - TestPerformanceOfStaticVsDynamicCalls::run@92 ; {poll}0x0000000002d01065: cmp $0x3b9aca00,%rbx0x0000000002d0106c: jl 0x0000000002d01054循环产生额外的寄存器溢出:
0x0000000002d011d0: mov 0x28(%rsp),%r11 <---- the problem is here0x0000000002d011d5: add %r10,%r110x0000000002d011d8: add $0x1,%r100x0000000002d011dc: add $0x1,%r110x0000000002d011e0: mov %r11,0x28(%rsp) ;*ladd ; - TestPerformanceOfStaticVsDynamicCalls::addStatic@2 ; - TestPerformanceOfStaticVsDynamicCalls::run@330x0000000002d011e5: mov 0x28(%rsp),%r11 <---- the problem is here0x0000000002d011ea: add $0x1,%r11 ; OopMap{[32]=Oop off=526} ;*goto ; - TestPerformanceOfStaticVsDynamicCalls::run@440x0000000002d011ee: test %eax,-0x1c911f4(%rip) # 0x0000000001070000 ;*goto ; - TestPerformanceOfStaticVsDynamicCalls::run@44 ; {poll}0x0000000002d011f4: cmp $0x3b9aca00,%r100x0000000002d011fb: jl 0x0000000002d011d0 ;*ifge ; - TestPerformanceOfStaticVsDynamicCalls::run@25PS 以下JVM选项对于分析JIT编译很有用:
-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining -XX:+PrintAssembly -XX:Compileonly=TestPerformanceOfStaticVsDynamicCalls



