JMH如何使用

JMH的基本用法

基础注解 @Benchmark

在一个基本测试类中至少包含一个被@Benchmark标记的方法,否则会抛出异常。

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class JMHExample02 { public void normalMethod() {
} public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder().include(JMHExample02.class.getSimpleName())
.forks(1)
.measurementIterations(10)
.warmupIterations(10)
.build();
new Runner(opts).run();
}
}

异常:Exception in thread "main" No benchmarks to run; check the include/exclude regexps.

Warmup以及Measurement

Warmup: "热身",使得在度量之前,类经历了早期的优化、JVM运行期编译、JIT优化

Measurement: 真正的度量操作,度量过程中的所有数据都会被纳入统计

全局设置

  1. 构造Options时设置批次执行

    final Options opts = new OptionsBuilder().include(JMHExample02.class.getSimpleName())
    .forks(1)
    //度量执行批次为5
    .measurementIterations(5)
    //在度量之前先执行两次热身
    .warmupIterations(2)
    .build();
    new Runner(opts).run();
  2. @Warmup与@Measurement注解

    @BenchmarkMode(Mode.AverageTime)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    @State(Scope.Thread) @Measurement(iterations = 5)
    @Warmup(iterations = 2)
    public class JMHExample02

局部设置(基准测试方法之上)

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
public class JMHExample03 { @Benchmark
public void test1() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(10);
} @Benchmark
@Warmup(iterations = 5)
@Measurement(iterations = 4)
public void test2() throws InterruptedException {
TimeUnit.MILLISECONDS.sleep(1);
}
public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder().include(JMHExample03.class.getSimpleName())
.forks(1)
//.measurementIterations(2)
//.warmupIterations(2)
.build();
new Runner(opts).run();
}
}

执行结果:

基准测试方法 测试结果
test1 预热3次、度量5次
test2 预热5次、度量4次

Warmup以及Measurement在基准测试方法上的设置会覆盖全局设置,但是无法覆盖Options中构建的全局设置

BenchmarkMode

四种模式概念

模式 使用
AverageTime 平均响应时间(方法每一次调用)
Throughput 单位时间内方法的调用次数
SampleTime 抽样的方式统计性能数据
SingleShotTime 无论warmup还是measurement,每批次中,基准测试方法只会执行一次(warmup一般设置为0)

多模式设置

我们可以在基准测试方法上设置多个模式,甚至是全部

@BenchmarkMode({Mode.AverageTime,Mode.Throughput})
@Benchmark
public void testThroughputAndAverageTime(){
TimeUnit.MILLISECONDS.sleep(1);
}
@BenchmarkMode(Mode.All)
@Benchmark
public void testAll(){
TimeUnit.MILLISECONDS.sleep(1);
}

覆盖次序:基准方法上的设置会覆盖类上的设置,Options上的设置会覆盖所有的设置。

OutputTimeUnit

提供了统计结果输出时的时间单位,覆盖次序与BenchmarkMode一致

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
public class JMHExample04 { @OutputTimeUnit(TimeUnit.MICROSECONDS)
@Benchmark
public void test() throws InterruptedException {
TimeUnit.SECONDS.sleep(1);
} public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder().include(JMHExample04.class.getSimpleName())
.include(JMHExample04.class.getSimpleName())
.timeUnit(TimeUnit.NANOSECONDS)
.forks(1)
.build();
new Runner(opts).run();
}
}

三种State的使用

Thread独享(共享)的State

  1. Scope.Thread: 每个运行基准测试方法的线程都拥有一个独立的对象实例
  2. Scope.Benchmark: 多个线程共享同一个对象实例
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
//设置5个线程运行基准测试方法
@Threads(5)
public class JMHExample07 { @State(Scope.Benchmark)
//@State(Scope.Benchmark)
public static class Test {
public Test() {
System.out.println("create instance");
} public void method() {
}
} @Benchmark
public void test(Test test){
test.method();
} public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHExample07.class.getSimpleName())
.build();
new Runner(opts).run();
}
}

执行结果

​ @State(Scope.Thread)输出了5次 "create instance"

​ @State( Scope.Benchmark)只输出了1次 "create instance"

上述的“对象”是指被@State标记的类实例。可通过基准测试方法的参数引入(如上),或是直接运行基准测试方法所在的宿主class。

@Thread注解设置参与基准测试的线程数

线程组共享的State

  1. Scope.Group
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
//设置5个线程运行基准测试方法
@Threads(5)
public class JMHExample08 { @State(Scope.Group)
public static class Test {
public Test() {
System.out.println("create instance");
} public void read() {
System.out.println("test read");
} public void write() {
System.out.println("test write");
}
} @GroupThreads(3)
@Group("test")
@Benchmark
public void testRead(Test test){
test.read();
} @GroupThreads(3)
@Group("test")
@Benchmark
public void testWrite(Test test){
test.write();
}
public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHExample08.class.getSimpleName())
.build();
new Runner(opts).run();
}
}

执行结果:testRead()与testWrite()交替执行

前两种State的情况下,基准测试方法都只能按照顺序逐个执行。而想要多个方法并行地去访问共享数据,则需要Scope.Group

@Param的使用

假如我们现在想要对两个不同类型的Map进行微基准的性能测试,该怎么做呢?按照前面的方法,我们可以为两个Map分别编写微基准测试方法,如下:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
//设置5个线程运行基准测试方法
@Threads(5)
@State(Scope.Benchmark)
public class JMHExample09 { private Map<Long, Long> concurrentHashMap;
private Map<Long, Long> synchronizedMap;
@Setup
public void setUp(){
concurrentHashMap = new ConcurrentHashMap<>();
synchronizedMap = Collections.synchronizedMap(new HashMap<Long, Long>());
} @Benchmark
public void testConcurrentHashMap(){
this.concurrentHashMap.put(System.nanoTime(),System.nanoTime());
} @Benchmark
public void testSynchronizedMap(){
this.concurrentHashMap.put(System.nanoTime(),System.nanoTime());
}
public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHExample09.class.getSimpleName())
.build();
new Runner(opts).run();
}
}

但是当我们想对更多类型的集合(或是其他的东东)进行微基准测试时,这种方法显然就多了很多的冗余代码,此时我们就可以使用@Param来简化代码啦,如下:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Fork(1)
@Measurement(iterations = 5)
@Warmup(iterations = 3)
//设置5个线程运行基准测试方法
@Threads(5)
//多个线程共享实例
@State(Scope.Benchmark)
public class JMHExample10 { @Param({"1","2","3","4"})
private int type; Map<Object, Object> map = null;
@Setup
public void setUp(){
switch (type){
case 1:
this.map = new ConcurrentHashMap<>();
break;
case 2:
this.map = new ConcurrentSkipListMap<>();
break;
case 3:
this.map = new Hashtable<>();
break;
case 4:
this.map = Collections.synchronizedMap(new HashMap<>());
}
} @Benchmark
public void test(){
this.map.put(System.nanoTime(),System.nanoTime());
}
public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHExample10.class.getSimpleName())
.build();
new Runner(opts).run();
}
}

执行结果

结果中只截取了部分关键输出,多出的type列正是对应@Param所提供的参数

Benchmark                           (type)  Mode  Cnt       Score        Error  Units
JMHExample09.testConcurrentHashMap 1 avgt 5 23098.216 ± 161361.562 us/op
JMHExample09.testConcurrentHashMap 2 avgt 5 45467.394 ± 103183.828 us/op
JMHExample09.testConcurrentHashMap 3 avgt 5 61373.892 ± 243766.954 us/op
JMHExample09.testConcurrentHashMap 4 avgt 4 140614.207 ± 650830.876 us/op

各个字段含义

Mode Cnt Score Error Units type
模式(四种) 基准方法调用次数 响应时间 时间偏差 时间单位 @Param参数

有了@Param之后,我们只需要编写一次微基准测试方法即可,JMH会根据@Param提供的参数值自动执行基准测试以及统计。

JMH的测试套件

Setup以及TearDown

  1. @Setup: 基准测试方法之前调用,通常用于资源的初始化。
  2. @TearDown: 基准测试方法之后调用,通常用于资源的回收清理工作。
@BenchmarkMode(Mode.SingleShotTime)
@Fork(1)
@Warmup(iterations = 5)
@Measurement(iterations = 10)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class JMHExample11 { private List<String> list = null;
@Setup
public void setUp(){
this.list = new ArrayList<>();
System.out.println("setUp...");
} @Benchmark
public void testRight() {
this.list.add("Test");
} @Benchmark
public void testWrong() {
//do nothing here
} @TearDown
public void tearDown() {
System.out.println("tearDown...");
assert this.list.size() > 0 : "The Size Of List Must Lager Than Zero";
} public static void main(String[] args) throws RunnerException {
final Options opts = new OptionsBuilder()
.include(JMHExample11.class.getSimpleName())
.jvmArgs("-ea") //enable assertion 激活断言
.build();
new Runner(opts).run();
}
}

执行结果

# Warmup Iteration   1: setUp...
4.900 us/op
# Warmup Iteration 2: 0.600 us/op
# Warmup Iteration 3: 0.600 us/op
# Warmup Iteration 4: 0.400 us/op
# Warmup Iteration 5: 0.500 us/op
Iteration 1: 0.600 us/op
Iteration 2: 0.900 us/op
Iteration 3: 0.600 us/op
Iteration 4: 0.400 us/op
Iteration 5: 0.500 us/op
Iteration 6: 0.400 us/op
Iteration 7: 0.300 us/op
Iteration 8: 0.500 us/op
Iteration 9: 0.400 us/op
Iteration 10: tearDown...

使用Level控制测试套件

通过上面的结果可以知道,默认情况下,@Setup@TearDown的套件方法分别在所有执行批次之前与之后执行。JMH还提供了另外两种配置。

  1. Trial:默认配置。@Setup(Level.Trial)

  2. Iteration:因为可以设置Warmup与Measurement,所以基准方法可能被执行若干个批次。Iteration将允许我们在每个批次前后执行套件方法。

    @Setup(Level.Iteration)
    public void setUp()
  3. Invocation:每个批次(Warmup或Measurement)中都可能存在多次基准方法的调用,Invocation将允许我们在每一次调用基准方法的前后执行套件。

    @Setup(Level.Invocation)
    public void setUp()

具体代码不做展示。

JMH如何使用的更多相关文章

  1. [译]使用JMH进行微基准测试:不要猜,要测试!

    英文原文:Micro Benchmarking with JMH: Measure, don't guess!翻译地址:使用JMH进行微基准测试:不要猜,要测试!原文作者:Antonio翻译作者:Ho ...

  2. 用 JMH 检测 Lambdas 序列化性能

    本文将介绍如何进行 Java Lambdas 序列化性能检测.Lambdas 的重要性以及 Lambdas 在分布式系统中的应用. Lambdas 表达式是 Java 8 中万众期待的新特性,其若干用 ...

  3. 【Code Tools】Java微基准测试工具JMH之高级篇

    一.IntelliJ IDEA JMH Plugin 这是一个插件,允许您以与JUnit相同的方式使用JMH.以下是已经实现的功能: 1.@Benchmark method generation(自动 ...

  4. 【Code Tools】Java微基准测试工具JMH之中级篇

    一.JMH中的基本概念 1)Mode Mode 表示 JMH 进行 Benchmark 时所使用的模式.通常是测量的维度不同,或是测量的方式不同.目前 JMH 共有四种模式: 1.Throughput ...

  5. 【Code Tools】Java微基准测试工具JMH之入门篇

    一.JMH是什么 JMH是一个Java工具,用于构建.运行和分析用Java和其他语言编写的以JVM为目标的 nano/micro/milli/macro 基准测试. 二.基本注意事项 1)运行JMH基 ...

  6. JMH实践-代码性能测试工具

    概述 JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件 JMH比较典型的应用场景有: 想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的 ...

  7. 基于JMH的Benchmark解决方案

    原始Benchmark做法 在设计新框架的时候,往往需要评估待接入的组件的性能,这个时候我们可能会利用UnitTest来进行,写一个方法,然后在循环里面跑,利用System.CurrentTimeMi ...

  8. JMH使用说明

    JMH使用说明 一.概述 JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件.何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测 ...

  9. JMH 使用指南 - java 性能测试

    JMH 篇 JMH,即Java Microbenchmark Harness 翻译:java 微基准测试 工具套件.## 1.添加依赖```<dependency> <groupId ...

  10. JMH 性能测试框架

    参考 1 Java 并发编程笔记:JMH 性能测试框架  http://blog.dyngr.com/blog/2016/10/29/introduction-of-jmh/ 2  Code Samp ...

随机推荐

  1. solana杂谈(1)

    solana杂谈(1) 本文适用于"只需大致了解 Solana"的读者,部分说法可能不够准确或不够深入.如需详细了解,建议阅读 Solana 的官方文档:https://solan ...

  2. Spring Boot 自动配置原理深度解析

    在 Spring 生态系统中,Spring Boot 自动配置是实现 "零配置" 开发的核心特性,通过约定大于配置的理念,自动为应用注入所需的基础设施配置.本文从核心注解.底层机制 ...

  3. [arc133e]Cyclic Medians

    E - Cyclic Medians 看到中位数,就是经典套路:将\(\geq\)中位数的都赋值为\(1\),\(<\)的赋值为\(0\) 那么对于数\(A\),就等于\(\sum_{i=1}^ ...

  4. 二、Linux基本应用工具

    1.系统文件共享(网络) 通过网络文件共享协议(例如 SMB 或 NFS)来完成Ubuntu下的文件夹共享给 Windows 1.Samba 实现共享 安装samaba sudo apt update ...

  5. C# 对比两个byte[]是否一样

    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] static extern int B ...

  6. C# 用Linq或Lambda查询DataGridView行中的数据是否包含(各种操作)

    http://blog.csdn.net/xht555/article/details/38685845 https://www.cnblogs.com/wuchao/archive/2012/12/ ...

  7. 想让自己的Ubuntu更漂亮嘛?

    近期没啥大项目在进行,今天闲来无事,值班空闲时间,忽然发现自己的Ubuntu竟是如此的"丑"(虽然用过这么多Linux发行版,但不得不承认Ubuntu唯一的优点就是好看!),于是准 ...

  8. Codeforces Round #568 (Div. 2) AB C1 C2 题解

    传送门 A. Ropewalkers 题意:给三个数,每次可以对一个数+1或-1,问最少多少次可以使得三个数两两之间距离>=d. 思路:水题,存进来的排个序,abc依次表示从小到大的.只要考虑b ...

  9. 实战 PCA

    简介 PCA code #加载数据 import pandas as pd import numpy as np data = pd.read_csv('iris_data.csv') data.he ...

  10. Semantic Kernel Agent Orchestration编排

    一.多代理编排核心价值 Semantic Kernel的Agent Orchestration框架解决了传统单代理系统的局限性: // 统一调用接口示例(适用于所有模式) InProcessRunti ...