Gravitino 如何进行 JMH 测试


概览

Gravitino JMH 目录包含一套基于 JMH 的微基准测试(Microbenchmark)套件,专门用于评估 Gravitino 中核心子系统的性能特征,如:实体缓存(Entity Cache)、元数据解析逻辑以及并发控制机制等。

执行方式

使用如下命令运行 JMH 测试:

./gradlew :core:jmh

测试完成后,结果将保存于:build/reports/jmh 目录中。

示例报告格式

Benchmark                                         (totalCnt)  Mode  Cnt        Score        Error  Units
EntityCacheGetBenchmark.benchmarkGet                     100  thrpt   10  17906401.139 ±  905521.957  ops/s
EntityCachePutBenchmark.benchmarkPut                   1000  avgt    10       0.002 ±     0.001   s/op
  • Score:每秒操作数或每次调用耗时;
  • Error:误差范围(通常为 99.9% 置信区间),即结果可信区间为 Score ± Error;

例如:

Score: 19008727.28 ops/s
Error: ± 1172892.70 ops/s
=> 实际性能大概率处于 17,835,834 ~ 20,181,620 ops/s 之间

误差越小,说明测试结果越稳定可靠。

什么是 JMH?

JMH(Java Microbenchmark Harness)是 OpenJDK 团队专为 Java 微基准测试开发的框架,主要用于精确测量方法调用、缓存访问、对象分配等细粒度操作的性能。

相较于传统测试方式,JMH 能处理如下影响因素:

  • JIT 编译和 JVM 热身(warm-up)
  • 死代码消除(Dead Code Elimination)
  • 逃逸分析(Escape Analysis)
  • 线程争用和伪共享(False Sharing)

JMH 提供基于注解的 API,并自动处理多次迭代、预热、JVM fork 和统计报告,已被广泛应用于 JDK、Spring、Netty、Lucene 等高性能项目。

示例基准代码

@BenchmarkMode({Mode.Throughput, Mode.AverageTime})
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class StringConnectTest {

  @Param({"10", "50", "100"})
  private int length;

  @Benchmark
  public void testStringAdd(Blackhole blackhole) {
    String a = "";
    for (int i = 0; i < length; i++) {
      a += i;
    }
    blackhole.consume(a);
  }

  @Benchmark
  public void testStringBuilderAdd(Blackhole blackhole) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < length; i++) {
      sb.append(i);
    }
    blackhole.consume(sb.toString());
  }
}

常用注解说明

注解 说明
@Benchmark 声明基准方法,JMH 会多次调用并测量其性能
@BenchmarkMode 设置测量模式,如吞吐量(Throughput)、平均时间(AverageTime)
@OutputTimeUnit 设置时间单位,如秒(SECONDS)、毫秒、纳秒等
@State 设置对象共享粒度,常用为 Scope.Thread(每线程独立)
@Param 参数化测试,生成多组输入用例

BenchmarkMode 模式说明

模式 含义
Throughput 吞吐量,每秒完成操作数
AverageTime 每次操作的平均耗时
SampleTime 对多个样本测量操作耗时,展示分布
SingleShotTime 单次调用耗时,适合冷启动分析
All 执行所有模式

在 Gravitino 中使用 JMH

Gravitino 使用 jmh-gradle-plugin 集成 JMH 到 Gradle 构建中。

插件配置

在 build.gradle 中添加:

plugins {
  id "me.champeau.jmh" version "0.7.3"
}

注意:0.6.0 之前版本的插件 ID 为 me.champeau.gradle.jmh

目录结构

基准测试文件需放置于以下目录:

src/jmh/java       # Java 测试代码
src/jmh/resources  # 资源文件(可选)

依赖引入

若基准测试依赖第三方库,如 commons-io,可在 dependencies 中添加:

dependencies {
  jmh 'commons-io:commons-io:2.7'
}

要指定 JMH 版本:

dependencies {
  jmh 'org.openjdk.jmh:jmh-core:1.37'
  jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'
}

JMH 测试任务

任务名 说明
jmhClasses 编译测试源代码
jmhRunBytecodeGenerator 运行字节码生成器
jmhCompileGeneratedClasses 编译生成代码
jmhJar 打包成可运行 JMH jar
jmh 执行所有基准测试(主任务)

执行命令:

./gradlew :core:jmh

常用配置项

jmh {
  includes = ['.*Benchmark']     // 正则匹配要执行的测试类
  iterations = 10                // 测量阶段迭代次数
  warmupIterations = 5           // 预热阶段迭代次数
  fork = 1                       // JVM 启动次数(独立运行)
  threads = 4                    // 工作线程数
  resultFormat = 'CSV'          // 报告格式(CSV、JSON、TEXT等)
  resultsFile = file("$buildDir/reports/jmh/results.csv")
  includeTests = true            // 是否包含 test 目录中的类
  duplicateClassesStrategy = DuplicatesStrategy.FAIL
}

更多映射与命令行选项见:配置选项映射表

JMH 补充说明

为什么需要 Warm-up(预热)?

由于 JVM 存在 JIT 编译机制,频繁调用的方法可能会被编译为本地代码。预热阶段可确保测试结果更贴近生产环境下的真实性能。

可视化工具

支持导入 JSON/CSV 报告生成图表。

IntelliJ 插件支持

安装 JMH 插件后,可像运行 JUnit 一样运行基准测试:

  • 自动生成带 @Benchmark 的方法模板
  • 单独运行方法或类中的所有测试
  • 插件路径:File → Settings → Plugins → 搜索 JMH

文章作者: pancx
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 pancx !
评论
  目录