java - 如何在Java中编写正确的微基准测试?

如何在Java中编写(并运行)正确的微基准?
我正在寻找一些代码示例和注释,说明需要考虑的各种问题。
示例:基准应该度量时间/迭代还是迭代/时间,为什么?
相关:Is stopwatch benchmarking acceptable?


最佳答案:

关于编写微基准的提示:
规则0:阅读一篇关于JVM和微观基准的著名论文。一个好的是from the creators of Java HotSpot。不要对微观基准期望太高;它们只测量有限范围的JVM性能特性。
规则1:始终包括一个预热阶段,该阶段一直运行测试内核,足以在计时阶段之前触发所有初始化和编译。(在预热阶段,可以减少迭代次数。经验法则是数万次内部循环迭代。)
规则2:始终使用-XX:+PrintCompilation-verbose:gc等运行,这样您就可以验证编译器和JVM的其他部分在您的计时阶段没有做意外的工作。
规则2.1:在计时和预热阶段的开始和结束时打印消息,以便您可以验证在计时阶段没有来自规则2的输出。
规则3:注意-client-server以及osr和常规编译之间的区别。-XX:+PrintCompilation标志报告带有at符号的OSR编译,以表示非初始入口点,例如:Trouble$1::run @ 2 (41 bytes)。如果您追求最好的性能,那么您更喜欢服务器而不是客户机,更喜欢常规的OSR。
规则4:注意初始化效果。不要在计时阶段第一次打印,因为打印将加载并初始化类。不要在预热阶段(或最终报告阶段)之外加载新类,除非您专门测试类加载(在这种情况下,只加载测试类)。规则2是你对这种影响的第一道防线。
规则5:注意去优化和重新编译的效果。在计时阶段,不要第一次使用任何代码路径,因为编译器可能会基于先前乐观的假设(即根本不会使用该路径),对代码进行垃圾处理和重新编译。规则2是你对这种影响的第一道防线。
规则6:使用适当的工具阅读编译器的思想,并期望对它生成的代码感到惊讶。在形成使事情更快或更慢的理论之前,先自己检查代码。
规则7:减少测量中的噪音。在一台安静的机器上运行基准测试,并运行几次,丢弃异常值。使用-Xbatch将编译器与应用程序一起序列化,并考虑设置-XX:CICompilerCount=1以防止编译器与自身并行运行。尽量减少GC开销,将Xmx(足够大)设置为Xms,如果可用,则使用Brian Goetz, 2005
规则8:为基准使用一个库,因为它可能更有效,并且已经为此目的进行了调试。例如UseEpsilonGCJMHCaliper