JMH基准测试入门配置及示例

JMH的全称是Java Microbenchmark Harness,即Java微基准测试工具。

JMH是一个用于构建,运行和分析基于Java及其他JVM语言的基准测试工具。它也是OpenJDK项目的一部分。

要运行JMH基准测试,推荐的方法是使用Maven来构建一个测试项目。生成相关的依赖信息,以及简单的测试骨架代码。由于这种方式比较纯粹,项目是全新的、自动生成的,不受其他环境的影响,因此比较可靠。

使用Maven创建一个JMH基准测试项目

使用以下Maven命令创建一个JMH测试项目。

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

其中,以archetype开头的3个参数为JMH依赖信息,JMH版本号为1.21。

-DarchetypeGroupId=org.openjdk.jmh -DarchetypeArtifactId=jmh-java-benchmark-archetype -DarchetypeVersion=1.21

最后3个参数为测试项目的信息,分别是默认包名、项目名和版本号。

-DgroupId=org.sample -DartifactId=test -Dversion=1.0

1)生成的pom.xml主要代码如下

<groupId>org.sample</groupId>
<artifactId>test</artifactId>
<version>1.0</version>
<packaging>jar</packaging>

<name>JMH benchmark sample: Java</name>

<!-- This is the demo/sample template build script for building Java benchmarks with JMH.
   Edit as needed. -->

<dependencies>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-core</artifactId>
        <version>{jmh.version}</version>
    </dependency>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-generator-annprocess</artifactId>
        <version>{jmh.version}</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <!-- JMH version to use with this project. -->
    <jmh.version>1.21</jmh.version>
    <!-- Java source/target to use for compilation. -->
    <javac.target>1.8</javac.target>
    <!-- Name of the benchmark Uber-JAR to generate. -->
    <uberjar.name>benchmarks</uberjar.name>
</properties>

2)生成一个MyBenchmark.java类

默认生成一个测试类,类里面仅有一个测试方法testMethod(),该方法带有一个@Benchmark注解。

可以在testMethod()方法内添加想要测试的代码逻辑,执行基准测试的时候将会执行该方法。

package org.sample;

import org.openjdk.jmh.annotations.Benchmark;

public class MyBenchmark {

    @Benchmark
    public void testMethod() {
        // This is a demo/sample template for building your JMH benchmarks. Edit as needed.
        // Put your benchmark code here.
    }

}

编译构建项目

编译项目。

$ cd test/
$ mvn clean verify

生成目标信息如下。

$ ls target/
benchmarks.jar  generated-sources/  maven-status/
classes/        maven-archiver/     test-1.0.jar

其中,benchmarks.jar包含了待测试代码的class文件,以及执行测试需要依赖的JMH相关class文件。

执行基准测试

在完成代码的构建之后,可以执行以下命令执行测试。

执行命令后,JMH将会扫描并找到所有待测试的代码,并执行测试相应的方法。

执行的过程中,会输出测试相关数据,总体上可以分为3部分:

  1. 测试环境配置信息;
  2. 每轮测试的具体情况;
  3. 整体测试结果。

测试环境配置信息

通过输出信息,可以了解到测环境试配置如下:

  • JMH版本号为1.21;
  • JDK版本号为1.8.0_121;
  • 热身:每轮测试进行5次热身迭代,每次迭代的时间为10s;
  • 真正的基准测试:每轮测试进行5次基准迭代,每次迭代的时间为10s;
  • 超时:每次迭代的超时时间为10分钟;
  • 线程:执行测试的线程数为1,顺序执行每轮测试;
  • 基准测试模式:默认进行吞吐量测试;
  • 基准测试方法:org.sample.MyBenchmark.testMethod
$ java -jar target/benchmarks.jar
# JMH version: 1.21
# VM version: JDK 1.8.0_121, Java HotSpot(TM) 64-Bit Server VM, 25.121-b13
# VM invoker: C:\SDK\jdk1.8.0_121\jre\bin\java.exe
# VM options: <none>
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: org.sample.MyBenchmark.testMethod

# Run progress: 0.00% complete, ETA 00:08:20
# Fork: 1 of 5
# Warmup Iteration   1: 1576551518.400 ops/s
# Warmup Iteration   2: 1529719054.021 ops/s
# Warmup Iteration   3: 1666073877.889 ops/s
# Warmup Iteration   4: 1640435331.734 ops/s
# Warmup Iteration   5: 1638832864.366 ops/s
Iteration   1: 1625641896.790 ops/s
Iteration   2: 1533553941.136 ops/s
Iteration   3: 1592753193.369 ops/s
Iteration   4: 1632034409.677 ops/s
Iteration   5: 1595397793.688 ops/s

# Run progress: 20.00% complete, ETA 00:06:44
# Fork: 2 of 5
# Warmup Iteration   1: 1464189837.888 ops/s
# Warmup Iteration   2: 1568131253.159 ops/s
# Warmup Iteration   3: 1512431773.674 ops/s
# Warmup Iteration   4: 1624047095.614 ops/s
# Warmup Iteration   5: 1599319656.890 ops/s
Iteration   1: 1549565370.435 ops/s
Iteration   2: 1479685624.920 ops/s
Iteration   3: 1546268750.693 ops/s
Iteration   4: 1624076911.097 ops/s
Iteration   5: 1547120121.585 ops/s

# Run progress: 40.00% complete, ETA 00:05:03
# Fork: 3 of 5
# Warmup Iteration   1: 1635927468.533 ops/s
# Warmup Iteration   2: 2117152863.952 ops/s
# Warmup Iteration   3: 2191950165.947 ops/s
# Warmup Iteration   4: 2117139604.170 ops/s
# Warmup Iteration   5: 1966743425.584 ops/s
Iteration   1: 2094680040.904 ops/s
Iteration   2: 2192052945.492 ops/s
Iteration   3: 2215953631.021 ops/s
Iteration   4: 2187306852.799 ops/s
Iteration   5: 2225823062.910 ops/s

# Run progress: 60.00% complete, ETA 00:03:22
# Fork: 4 of 5
# Warmup Iteration   1: 2215218138.467 ops/s
# Warmup Iteration   2: 2060564779.974 ops/s
# Warmup Iteration   3: 2128581454.514 ops/s
# Warmup Iteration   4: 2136226391.233 ops/s
# Warmup Iteration   5: 2190998438.402 ops/s
Iteration   1: 2149230777.286 ops/s
Iteration   2: 1962048343.572 ops/s
Iteration   3: 1632748373.818 ops/s
Iteration   4: 2069121825.198 ops/s
Iteration   5: 2137771926.471 ops/s

# Run progress: 80.00% complete, ETA 00:01:40
# Fork: 5 of 5
# Warmup Iteration   1: 2175401658.601 ops/s
# Warmup Iteration   2: 1998795501.979 ops/s
# Warmup Iteration   3: 2207762443.100 ops/s
# Warmup Iteration   4: 2158909861.991 ops/s
# Warmup Iteration   5: 2172243775.496 ops/s
Iteration   1: 2088490735.383 ops/s
Iteration   2: 2055344061.187 ops/s
Iteration   3: 2143537771.341 ops/s
Iteration   4: 2249547560.686 ops/s
Iteration   5: 2204700995.400 ops/s


Result "org.sample.MyBenchmark.testMethod":
  1893378276.674 ±(99.9%) 219511037.182 ops/s [Average]
  (min, avg, max) = (1479685624.920, 1893378276.674, 2249547560.686), stdev = 293040954.374
  CI (99.9%): [1673867239.493, 2112889313.856] (assumes normal distribution)


# Run complete. Total time: 00:08:24

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.testMethod  thrpt   25  1893378276.674 ± 219511037.182  ops/s

每轮测试的具体情况

总共运行了5轮测试,每轮测试的输出信息大致如下:

  • 当前的测试进度,以及剩下的测试时间;
  • 当前是第几轮测试;
  • 热身标识和序号,以及QPS;
  • 基准测试标识和序号,以及QPS。

进行多轮测试,可以确保结果不是随机的。

每一轮会先执行热身,防止受启动和运行波动的影响

热身之后的基准迭代测试,才会反应到测试结果中。

每1次迭代时间为10s,每一轮进行5次热身5次基准测试,大约占用100s。

整体测试结果

汇总本次基准测试的整体情况,包括测试运行时间和每个测试方法的QPS等信息。

Result块是基准测试目标方法的测试结果,其中(min, avg, max)是最小、平均和最大QPS。

本次基准测试,总共运行了8分钟24秒。

MyBenchmark.testMethod进行的是吞吐量(thrpt)测试,进行了25次基准迭代。

参考

https://github.com/openjdk/jmh


---转载本站文章请注明作者和出处 二进制之路(binarylife.icu),请勿用于任何商业用途---

留下评论