IMLC.ME

如何使用 JMH 进行 Java 基准性能测试(benchmarking)

本文介绍了如何使用 Java Microbenchmark Harness(JMH) 来测试 Java 代码的基准性能。 基本上就是官方 README.md (https://github.com/openjdk/jmh) 的精简翻译。

Java Microbenchmark Harness(JMH) 是一款用来构建、运行和分析 Java (或其他基于JVM)代码性能的测试工具。 JMH 支持 nano/micro/milli/macro 等精度的性能测试。其执行结果会助你更好地理解自己的代码性能,在性能调优的时候更有科学依据。

概览

JMH 提供了 @Benchmark 和其他与测试相关的一系列注解。开发者可以跟写 unit test 一样编写性能测试用例。

public class MyBenchmark {

  @Benchmark
  @Warmup(iterations = 1)
  @Fork(1)
  @Measurement(iterations = 1, time = 1, timeUnit = TimeUnit.MINUTES)
  @OutputTimeUnit(TimeUnit.NANOSECONDS)
  public void UUID() {
    UUID.randomUUID();
  }

}

测试的结果如下方日志所示。

/Users/myname/workspace/jdk/build/macosx-x86_64-server-release/jdk/bin/java -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62626:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -classpath /Users/myname/workspace/hellojmh/target/classes:/Users/myname/.m2/repository/org/openjdk/jmh/jmh-core/1.29/jmh-core-1.29.jar:/Users/myname/.m2/repository/net/sf/jopt-simple/jopt-simple/4.6/jopt-simple-4.6.jar:/Users/myname/.m2/repository/org/apache/commons/commons-math3/3.2/commons-math3-3.2.jar org.openjdk.jmh.Main me.imlc.MyBenchmark.*
# JMH version: 1.29
# VM version: JDK 17-internal, OpenJDK 64-Bit Server VM, 17-internal+0-adhoc.myname.jdk
# VM invoker: /Users/myname/workspace/jdk/build/macosx-x86_64-server-release/jdk/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=62626:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 1 iterations, 10 s each
# Measurement: 1 iterations, 1 min each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: me.imlc.MyBenchmark.UUID

# Run progress: 0.00% complete, ETA 00:01:10
# Fork: 1 of 1
# Warmup Iteration   1: 0.004 ops/ns
Iteration   1: 0.005 ops/ns


Result "me.imlc.MyBenchmark.UUID":
  0.005 ops/ns


# Run complete. Total time: 00:01:11

REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
experiments, perform baseline and negative tests that provide experimental control, make sure
the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
Do not assume the numbers tell you what you want them to tell.

Benchmark          Mode  Cnt  Score   Error   Units
MyBenchmark.UUID  thrpt       0.005          ops/ns

诚如上方 REMEMBER 所言,性能测试的结果受各种情况影响,切记不要盲目相信测试结果。

如何使用 JMH

在 JMH 的 README.md 中,官方推荐以命令行的方式使用 JMH。简而言之,把代码构建成 jar,然后 java -jar <...>.jar 执行。

初次接触 JMH,可以使用 Maven archetype 生成 Maven 项目。

  1. 官方文档中的示例代码没有指定 -DarchetypeVersion=1.29。默认生成的 JMH 项目,支持的 JDK 版本, Maven 插件的版本和使用方式都太旧。

为确保你能以最新的模板生成项目,请到 https://repo.maven.apache.org/maven2/org/openjdk/jmh/jmh-java-benchmark-archetype/ 检查最新的版本号 2. 你可以在上方地址看到其他的 archetypes。例如 Groovy、Kotlin 或者 Scale。如需测试其他代码,修改 -DarchetypeArtifactId=jmh-java-benchmark-archetype 即可。

mvn archetype:generate \
  -DinteractiveMode=false \
  -DarchetypeGroupId=org.openjdk.jmh \
  -DarchetypeArtifactId=jmh-java-benchmark-archetype \
  -DarchetypeVersion=1.29 \
  -DgroupId=org.sample \
  -DartifactId=test \
  -Dversion=1.0

上方命令执行完成后,你会在当前目录找到一个刚生成的新鲜热辣的 test 文件夹。简而言之,它就是一个普通的 Maven 项目。 项目里的 src/main/java/org/sample/MyBenchmark.java 为生成的测试用例文件。你可以直接在该文件中添加你要测试的代码。 然后,

cd test/
mvn clean verify
java -jar target/benchmarks.jar

通过 Maven 构建 jar 包,然后执行,略等片刻,你就可以看到基准性能测试的结果。

附加 "-h" 参数,你可以看到可用的命令行参数:

java -jar target/benchmarks.jar -h