JMH如何使用
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: 真正的度量操作,度量过程中的所有数据都会被纳入统计
全局设置
构造Options时设置批次执行
final Options opts = new OptionsBuilder().include(JMHExample02.class.getSimpleName())
.forks(1)
//度量执行批次为5
.measurementIterations(5)
//在度量之前先执行两次热身
.warmupIterations(2)
.build();
new Runner(opts).run();
@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
- Scope.Thread: 每个运行基准测试方法的线程都拥有一个独立的对象实例
- 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
- 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
@Setup: 基准测试方法之前调用,通常用于资源的初始化。@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还提供了另外两种配置。
Trial:默认配置。@Setup(Level.Trial)
Iteration:因为可以设置Warmup与Measurement,所以基准方法可能被执行若干个批次。Iteration将允许我们在每个批次前后执行套件方法。
@Setup(Level.Iteration)
public void setUp()
Invocation:每个批次(Warmup或Measurement)中都可能存在多次基准方法的调用,Invocation将允许我们在每一次调用基准方法的前后执行套件。
@Setup(Level.Invocation)
public void setUp()
具体代码不做展示。
JMH如何使用的更多相关文章
- [译]使用JMH进行微基准测试:不要猜,要测试!
英文原文:Micro Benchmarking with JMH: Measure, don't guess!翻译地址:使用JMH进行微基准测试:不要猜,要测试!原文作者:Antonio翻译作者:Ho ...
- 用 JMH 检测 Lambdas 序列化性能
本文将介绍如何进行 Java Lambdas 序列化性能检测.Lambdas 的重要性以及 Lambdas 在分布式系统中的应用. Lambdas 表达式是 Java 8 中万众期待的新特性,其若干用 ...
- 【Code Tools】Java微基准测试工具JMH之高级篇
一.IntelliJ IDEA JMH Plugin 这是一个插件,允许您以与JUnit相同的方式使用JMH.以下是已经实现的功能: 1.@Benchmark method generation(自动 ...
- 【Code Tools】Java微基准测试工具JMH之中级篇
一.JMH中的基本概念 1)Mode Mode 表示 JMH 进行 Benchmark 时所使用的模式.通常是测量的维度不同,或是测量的方式不同.目前 JMH 共有四种模式: 1.Throughput ...
- 【Code Tools】Java微基准测试工具JMH之入门篇
一.JMH是什么 JMH是一个Java工具,用于构建.运行和分析用Java和其他语言编写的以JVM为目标的 nano/micro/milli/macro 基准测试. 二.基本注意事项 1)运行JMH基 ...
- JMH实践-代码性能测试工具
概述 JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件 JMH比较典型的应用场景有: 想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的 ...
- 基于JMH的Benchmark解决方案
原始Benchmark做法 在设计新框架的时候,往往需要评估待接入的组件的性能,这个时候我们可能会利用UnitTest来进行,写一个方法,然后在循环里面跑,利用System.CurrentTimeMi ...
- JMH使用说明
JMH使用说明 一.概述 JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件.何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测 ...
- JMH 使用指南 - java 性能测试
JMH 篇 JMH,即Java Microbenchmark Harness 翻译:java 微基准测试 工具套件.## 1.添加依赖```<dependency> <groupId ...
- JMH 性能测试框架
参考 1 Java 并发编程笔记:JMH 性能测试框架 http://blog.dyngr.com/blog/2016/10/29/introduction-of-jmh/ 2 Code Samp ...
随机推荐
- solana杂谈(1)
solana杂谈(1) 本文适用于"只需大致了解 Solana"的读者,部分说法可能不够准确或不够深入.如需详细了解,建议阅读 Solana 的官方文档:https://solan ...
- Spring Boot 自动配置原理深度解析
在 Spring 生态系统中,Spring Boot 自动配置是实现 "零配置" 开发的核心特性,通过约定大于配置的理念,自动为应用注入所需的基础设施配置.本文从核心注解.底层机制 ...
- [arc133e]Cyclic Medians
E - Cyclic Medians 看到中位数,就是经典套路:将\(\geq\)中位数的都赋值为\(1\),\(<\)的赋值为\(0\) 那么对于数\(A\),就等于\(\sum_{i=1}^ ...
- 二、Linux基本应用工具
1.系统文件共享(网络) 通过网络文件共享协议(例如 SMB 或 NFS)来完成Ubuntu下的文件夹共享给 Windows 1.Samba 实现共享 安装samaba sudo apt update ...
- C# 对比两个byte[]是否一样
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] static extern int B ...
- C# 用Linq或Lambda查询DataGridView行中的数据是否包含(各种操作)
http://blog.csdn.net/xht555/article/details/38685845 https://www.cnblogs.com/wuchao/archive/2012/12/ ...
- 想让自己的Ubuntu更漂亮嘛?
近期没啥大项目在进行,今天闲来无事,值班空闲时间,忽然发现自己的Ubuntu竟是如此的"丑"(虽然用过这么多Linux发行版,但不得不承认Ubuntu唯一的优点就是好看!),于是准 ...
- Codeforces Round #568 (Div. 2) AB C1 C2 题解
传送门 A. Ropewalkers 题意:给三个数,每次可以对一个数+1或-1,问最少多少次可以使得三个数两两之间距离>=d. 思路:水题,存进来的排个序,abc依次表示从小到大的.只要考虑b ...
- 实战 PCA
简介 PCA code #加载数据 import pandas as pd import numpy as np data = pd.read_csv('iris_data.csv') data.he ...
- Semantic Kernel Agent Orchestration编排
一.多代理编排核心价值 Semantic Kernel的Agent Orchestration框架解决了传统单代理系统的局限性: // 统一调用接口示例(适用于所有模式) InProcessRunti ...