如何合理地确定代码块已被JIT编译?

10

在进行 Java 代码性能测试时,您想要测试 JIT 编译的代码,而不是原始字节码。为了使字节码被编译,您必须通过多次执行代码来触发编译,并允许足够的 yield 时间使后台线程完成编译。

  • 需要对代码路径进行多少次“预热”执行才能“非常自信”地确保代码将被 JIT 编译?
  • 主线程的最小休眠时间是多少,以“非常自信”地确保编译已完成(假设代码块较小)?

我正在寻找一个阈值,在任何现代操作系统中都可以安全应用,例如 Mac OS 或 Windows 用于开发环境,Linux 用于 CI / 生产。

答案:
  • 至少需要对代码路径进行10次“预热”执行,才能“非常自信”地确保代码将被 JIT 编译
  • 主线程的最小休眠时间应该在100毫秒到200毫秒之间,以“非常自信”地确保编译已完成(假设代码块较小)

请注意,JVM 优化了类似于 Java 的字节码和常见程序的典型特征。根据字节码的特性,它可能永远不会被编译。 - Antimony
3个回答

14

由于OP的意图并非确定该块是否JIT编译,而是确保测量优化代码,因此我认为OP需要观看一些这些基准测试讲座

TL;DR版本:没有可靠的方法可以确定是否达到了“稳态”:

  • 您只能进行长时间的测量,以获得您的具体系统通常需要多长时间才能达到您可以声明为“稳定”的状态的近似估计。

  • 观察-XX:+PrintCompilation是不可靠的,因为您可能处于计数器仍在波动的阶段,而JIT正在等待编译下一批现在热门的方法。由于此原因,您可能会有许多预热高原。该方法甚至可以根据待机的分层编译器的数量重新编译多次。

  • 虽然人们可能会争论调用阈值,但这些东西也不可靠,因为涉及到分层编译,方法可能更早地在调用者中内联,概率计数器可能会错过更新等等。也就是说,关于-XX:CompileThreshold=#的常见智慧是不可靠的。

  • JIT编译不是您追求的唯一预热效果。自动GC启发式算法,调度器启发式算法等也需要预热。

获取一个微基准测试工具,这将使任务更加容易!


6
首先,以客户端或服务器模式运行的JVM的结果很可能会有所不同。其次,这个数字高度依赖于你的代码复杂性,恐怕你需要为每个测试用例进行探索性估计。一般来说,你的字节码越复杂,就可以应用更多的优化,因此你的代码必须变得相对较热,才能使JVM深入使用其工具箱。一个JVM可能会重新编译一段代码十几次。
此外,“真实世界”的编译取决于你的字节码运行的上下文。例如,当单态调用站点被提升为超多态调用站点时,编译可能会发生,从而观察到的编译实际上代表了一种反优化。因此,在假设微基准反映代码实际性能时要小心。

建议您使用CompilationMXBean而不是建议的标志,该方法允许您检查JVM编译所花费的时间。如果时间太长,请重复运行测试,直到该值足够稳定为止(请耐心等待!)。框架可以帮助您创建良好的基准测试。个人而言,我喜欢caliper。但是,永远不要相信您的基准测试。

根据我的经验,在模仿javac的习语时,自定义字节码表现最佳。关于这个问题,我可以提到一个轶事,我曾经为Java源代码写过自定义字节码的等效代码:

int[] array = {1, 2, 3};

javac 创建数组并使用 dup 分配每个值,但我将数组引用存储在本地变量中,并将其加载回操作数栈以分配每个值。该数组的大小比那个大,性能差异明显。

最后,在编写基准测试之前,我建议阅读这篇文章


4

我不确定具体的数字,但是在进行速度测试时,我的做法如下:

  • 使用-XX:-PrintCompilation 标志运行
  • 让JVM热身直到没有编译调试信息生成,并且尽可能使时间保持一致

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接