You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何确保JMH基准测试方法预热阶段被充分调用触发编译

最优实现方案

不需要拉高全局预热配置,两种精准适配的方案可以解决大输入场景预热不足的问题,不会额外增加小输入场景的运行耗时:


方案1:按参数值单独匹配预热规则

JMH本身支持为不同的@Param参数组合单独指定预热参数,无需全局生效:

  • 对于100、10000这类小规模输入,保留默认预热配置即可——这类场景下单次方法执行耗时极短,默认预热迭代内的调用次数早就超过C2 JIT的默认编译阈值(10000次调用),足够触发编译。
  • 仅针对1000000这类大规模输入,单独调高预热迭代次数,或者将预热统计模式从固定迭代数改为按运行时长统计,保证预热阶段compute方法的累计调用量达到编译阈值即可。

这里要注意JMH的迭代逻辑:默认每个预热迭代会持续调用被测方法直到单迭代时长(默认1s)耗尽。如果大输入下compute单次执行耗时1ms,单个迭代仅能完成1000次调用,那给大输入场景配置10次以上预热迭代就足够触发JIT,不需要给小输入场景也套这个配置。


方案2:试次初始化阶段手动触发预调用

如果不想拆分预热配置,可以直接在每个参数组合的试次启动逻辑里手动完成预调用,逻辑完全可控:

@State(Scope.Benchmark)
class MyBenchmark {
  @Param({"100", "10000", "1000000"})
  public int ARRAY_SIZE;
  public int[] INPUT;

  @Setup(Level.Trial) // 每个参数组合的整个试次只执行一次
  public void setUp() {
    INPUT = new int[ARRAY_SIZE];
    // 原有INPUT初始化逻辑省略

    // 仅大输入场景手动预跑,触发JIT编译
    if (ARRAY_SIZE >= 1_000_000) {
      // 调用次数留足余量,超过C2默认10000次的编译阈值
      for (int i = 0; i < 20_000; i++) {
        compute();
      }
    }
  }

  @Benchmark
  public void compute() {
    // 原有测试逻辑
  }
}

这种方式不会被JMH的全局迭代配置影响,注意不要把Setup的级别设为Level.Iteration,否则每次迭代前都会重复执行预调用,反而引入额外开销。如果担心手动预调用过程产生的内存垃圾干扰正式测量,可以在预调用循环结束后手动触发一次System.gc()提示JVM完成垃圾回收,再进入正式测量流程。


避坑提醒

不要为了凑调用次数把基准测试模式改成SingleShotTime,该模式默认不会等待JIT编译完成,测出来的是解释执行状态下的性能,完全不符合峰值性能测量的目标。

内容的提问来源于stack exchange,提问作者MattDs17

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 06:39:27