JVH(Java Microbenchmark Harness),一个OpenJDK发布的一个面向方法级别的性能测试工具。它封装了性能测试的基础设施,使开发人员更多地关注测试逻辑,减少不必要的开发工作,他使开发人员可以使用类似JUnit的方式运行性能测试,在使用插件后这种优势更加突出。

先看一个简单的示例:

--需添加maven依赖

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.21</version>
</dependency>

<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.21</version>
    <scope>provided</scope>
</dependency>
                

--java代码
package com.sht.study.jmh;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import org.openjdk.jmh.samples.JMHSample_01_HelloWorld;

public class T001HelloWorld {

    @Benchmark
    public void testNoting(){

    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(T001HelloWorld.class.getSimpleName())
                .forks(1)
                .build();

        new Runner(opt).run();
    }
}

在上述示例中:

  1. @Benchmark标签用在方法上,用来标志这是一个性能测试方法,也就是我们要测试的代码逻辑,每个测试中可以有多个Benchmark。
  2. 构建运行配置项并启动测试。options有很多配置项,我们会在以后逐渐进行讲解和总结

接下来,我们分析运行结果:

# JMH version: 1.21
# VM version: JDK 1.8.0_161, Java HotSpot(TM) 64-Bit Server VM, 25.161-b12
# VM invoker: C:\Program Files\Java\jdk1.8.0_161\jre\bin\java.exe
# VM options: -javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar=18856:C:\Program Files\JetBrains\IntelliJ IDEA 2019.1.3\bin -Dfile.encoding=UTF-8
# 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: com.sht.study.jmh.T001HelloWorld.testNoting

# Run progress: 0.00% complete, ETA 00:01:40
# Fork: 1 of 1
# Warmup Iteration   1: 3561497106.539 ops/s
# Warmup Iteration   2: 3717422700.770 ops/s
# Warmup Iteration   3: 3673042896.880 ops/s
# Warmup Iteration   4: 3675741454.988 ops/s
# Warmup Iteration   5: 3688327201.989 ops/s
Iteration   1: 3702673032.227 ops/s
Iteration   2: 3603146922.577 ops/s
Iteration   3: 3441450888.242 ops/s
Iteration   4: 3804941548.309 ops/s
Iteration   5: 3622635812.187 ops/s


Result "com.sht.study.jmh.T001HelloWorld.testNoting":
  3634969640.709 ±(99.9%) 516935555.554 ops/s [Average]
  (min, avg, max) = (3441450888.242, 3634969640.709, 3804941548.309), stdev = 134246522.178
  CI (99.9%): [3118034085.154, 4151905196.263] (assumes normal distribution)


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

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
T001HelloWorld.testNoting  thrpt    5  3634969640.709 ± 516935555.554  ops/s

Process finished with exit code 0

第一段内容展示测试的基本信息,如JDK的版本及JVM参数等。
还有如下与该测试强相关的配置信息:

Warmup: 5 iterations, 10 s each - - 代表五个热身迭代,每个迭代10固定耗时10秒。
Measurement: 5 iterations, 10 s each --代表五个测试迭代,每个迭代固定耗时10秒。
Timeout: 10 min per iteration -- ${代表每个迭代的超时时间为10分钟(不是固定10秒钟吗,超时是啥意思)
Threads: 1 thread, will synchronize iterations --使用一个线程,串行每个迭代
Benchmark mode: Throughput, ops/time --${color}${red}${测试模式}是吞吐量
Benchmark: com.sht.study.jmh.T001HelloWorld.testNoting --执行的方法

Run progress: 0.00% complete, ETA 00:01:40 --本次运行的进展,ETA是什么意思
Fork: 1 of 1 --启动一个jvm,当前是第一个

--预热迭代及其吞吐量,预热的目的是让Java虚拟机对测试代码进行足够多的优化,比如在预热后被预测代码应该得到了充分的JIT编译和优化
Warmup Iteration 1: 3561497106.539 ops/s
Warmup Iteration 2: 3717422700.770 ops/s
Warmup Iteration 3: 3673042896.880 ops/s
Warmup Iteration 4: 3675741454.988 ops/s
Warmup Iteration 5: 3688327201.989 ops/s

--测试迭代及其吞吐量
Iteration 1: 3702673032.227 ops/s
Iteration 2: 3603146922.577 ops/s
Iteration 3: 3441450888.242 ops/s
Iteration 4: 3804941548.309 ops/s
Iteration 5: 3622635812.187 ops/s

--测试结果汇总,最大、最小、标准差\总耗时等,平均值、stdev,CI
Result "com.sht.study.jmh.T001HelloWorld.testNoting":
3414697939.411 ±(99.9%) 346560991.541 ops/s [Average]
(min, avg, max) = (3259941224.808, 3414697939.411, 3481613462.859), stdev = 90000788.952
CI (99.9%): [3068136947.870, 3761258930.952] (assumes normal distribution)

Run complete. Total time: 00:01:40

--下面一段话的意思是,数据只是数据,我们需要明白这些数据产生的原理,才能更好地利用他们。
尤其不能武断地认为这些数据就应该如此而应该使用~~profilers ~~、析因实验、执行提供实验控制的极限测试和阴性实验、确保基准环境在jvm/os/hw级别是安全的,并征求领域专家的意见(等各种手段来确保数据的可靠性)。
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
T001HelloWorld.testNoting thrpt 5 3414697939.411 ± 346560991.541 ops/s

JMH关键概念

JMH虽然看起来简单,但是真要用的好还是非常有挑战的,我们需要了解一下各种概念和机制(逐步补充

  1. 模式
  2. 状态
  3. 伪共享优化
  4. 循环优化
posted on 2019-08-11 20:29  无锡宋某  阅读(352)  评论(0)    收藏  举报