基于JMH的Benchmark解决方案
原始Benchmark做法
在设计新框架的时候,往往需要评估待接入的组件的性能,这个时候我们可能会利用UnitTest来进行,写一个方法,然后在循环里面跑,利用System.CurrentTimeMillis()来评估组件性能。然而这种机制,只是跑在了主线程中,无法将组件的性能全部测算出来。当单线程测算的性能已经到达极限的瑟吉欧鸡皮,无论怎么增加循环次数,OPS都不会有显著的提升。
上面的方案不怎么靠谱后,我们转向了多线程测算。一般都是在本地开几个线程,然后循环处理。之后再利用System.CurrentTimeMillis()的差值来评估组件性能。此种方法虽然更为靠谱了一些,但是依然面临着样本循环次数小,统计难度大,统计分类不全的特点。如果想测算的更精细,怕是没有个一时半会,得不到什么有效结果。
很显然你,上面的方法,是生产力低下的做法,那么有什么方法能够一劳永逸呢?
JMH Benchmark做法
今天我们将会讲解基于openjdk构建的jmh benchmark的做法,此种做法在github上很流行,很多开源代码都会在readme中附带上自己的benchmark,通俗易懂,而且让我们对性能有大概的了解。究竟如何做到的呢?
首先,我们需要引入maven包:
<!--bench mark-->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.19</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.19</version>
<!--<scope>test</scope>-->
</dependency>
引入jmh-core和jmh-generator-annprocess的作用是利用其做真正的benchmark操作。注意,在运行的时候,需要注释掉scope,然后先clean,然后package,最后install,一定要进行install操作,否则会提示配置文件找不到的问题。benchmark类不要放到unittest目录下,否则启动报错。
然后,编写我们的benchmark代码,这里我以local cache为例来做介绍:
package com.jd.limitbuy.common.cache.offheap.local; import org.openjdk.jmh.annotations.*;
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 java.util.UUID;
import java.util.concurrent.TimeUnit; /**
* @author shichaoyang
* @Description: local cache组件的benchmark
* @date 2018-08-16 11:04
*/ @BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@State(Scope.Thread)
public class localCacheBenchmark { private LocalCacheBuilder localCacheBuilder; private LocalCacheWrapper localCacheWrapper; @Setup
public void init() { localCacheBuilder = new LocalCacheBuilder();
localCacheBuilder.Init(); localCacheWrapper = new LocalCacheWrapper(localCacheBuilder); localCacheWrapper.set("my_benchmark_key", "this is my first benchmark!!!!!", "localMap1"); localCacheWrapper.hset("my_benchmark_key_hash", "my_hash_key", "this is my first benchmark!!!!!", "localMap1"); localCacheWrapper.sadd("my_benchmark_key_set", "this is my hash benchmark!!!!!", "localMap1"); localCacheWrapper.zadd("my_benchmark_key_zset", "start_val", 100, "localMap1"); localCacheWrapper.zadd("my_benchmark_key_zset", "end_val", 101, "localMap1");
} /**
* GroupThreads 并发线程数设置为3,可以打出接口最大的ops
*/ @Benchmark
@GroupThreads(4)
public String testLocalCacheSet() {
localCacheWrapper.set(UUID.randomUUID().toString(), "this is my first benchmark!!!!!", "localMap1");
return "ok";
} /**
* GroupThreads 并发线程数设置为3,可以打出接口最大的ops
*/
@Benchmark
@GroupThreads(4)
public String testLocalCacheGet() {
return localCacheWrapper.get("my_benchmark_key", "localMap1");
} @Benchmark
@GroupThreads(4)
public String testLocalCacheHSet() {
localCacheWrapper.hset(UUID.randomUUID().toString(), UUID.randomUUID().toString(), "this is my hash benchmark!!!!!", "localMap1");
return "ok";
} @Benchmark
@GroupThreads(4)
public String testLocalCacheHGet() {
return localCacheWrapper.hget("my_benchmark_key_hash", "my_hash_key", "localMap1");
} @Benchmark
@GroupThreads(4)
public String testLocalCacheHGetAll() {
localCacheWrapper.hgetAll("my_benchmark_key_hash", "localMap1");
return "ok";
} @Benchmark
@GroupThreads(4)
public String testLocalCacheSAdd() {
localCacheWrapper.sadd("slkfjskldfjsdklf", UUID.randomUUID().toString(), "localMap1");
return "ok";
} @Benchmark
@GroupThreads(4)
public String testLocalCacheSmember() {
localCacheWrapper.smembers("my_benchmark_key_set", "localMap1");
return "ok";
} @Benchmark
@GroupThreads(4)
public String testLocalCacheZAdd() {
localCacheWrapper.zadd(UUID.randomUUID().toString(), UUID.randomUUID().toString(), 100, "localMap1");
return "ok";
} @Benchmark
@GroupThreads(4)
public String testLocalCacheZRange() {
localCacheWrapper.zrange("my_benchmark_key_zset", "start_val", "end_val", "localMap1");
return "ok";
} public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(localCacheBenchmark.class.getSimpleName())
.forks(1)
.build();
new Runner(opt).run();
}
}
BenchmarkMode(Mode.Throughput)设置,主要是为了测试方法的ops性能。
OutputTimeUnit(TimeUnit.SECONDS) 设置,主要是以秒为单位进行输出,其实就是ops(operation per second)。
Setup设置,主要是为了对进行benchmark的类进行初始化操作。 注意,要进行benchmark测试的类,必须使用带参构造注入方式来进行,不能使用@Resource或者@Autowired等方式来进行注入,否则运行起来的时候会报NullPointer Exception,因为jmh不支持这种方式。所谓的带参构造注入,就是形如下面的方式:
public class OffheapCacheWrapper implements OffheapCacheStrategy {
/**
* 构造注入
* @param offheapCacheBuilder
*/
public OffheapCacheWrapper(OffheapCacheBuilder offheapCacheBuilder) {
this.offheapCacheBuilder = offheapCacheBuilder;
}
/**
* 缓存构建器
*/
private OffheapCacheBuilder offheapCacheBuilder;
}
然后在使用的时候,就可以按照benchmark代码中的方式进行实例初始化了。
Benchmark设置,主要是为了表明,此方法要进行测算。
GroupThreads设置,主要是对当前方法使用的并发数,如果机器为4核,那么这个数设置为4是最合适的。这和我们本地开启4个多线程测试的原理是一样的。
最后就是main方法了。每个benchmark测算类里面都要包含一个main的入口方法。入口方法的写法可以按照如上的写法进行书写即可。之后可以运行此main方法,就可以看到benchmark开始了,显示日志如下:
# JMH version: 1.19
# VM version: JDK 1.8.0_162, VM 25.162-b12
# VM invoker: C:\Program Files\Java\jdk1.8.0_162\jre\bin\java.exe
# VM options: -Dvisualvm.id=95286622200089 -javaagent:D:\soft\IntelliJ IDEA 2017.2.4\lib\idea_rt.jar=51200:D:\soft\IntelliJ IDEA 2017.2.4\bin -Dfile.encoding=UTF-8
# Warmup: 20 iterations, 1 s each
# Measurement: 20 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 4 threads, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.jd.limitbuy.common.cache.offheap.local.localCacheBenchmark.testLocalCacheGet # Run progress: 0.00% complete, ETA 00:06:00
# Fork: 1 of 1
# Warmup Iteration 1: 4626030.786 ops/s
# Warmup Iteration 2: 5915177.466 ops/s
# Warmup Iteration 3: 5250390.707 ops/s
# Warmup Iteration 4: 5821984.889 ops/s
# Warmup Iteration 5: 5878264.192 ops/s
# Warmup Iteration 6: 5958235.775 ops/s
# Warmup Iteration 7: 5872995.249 ops/s
# Warmup Iteration 8: 5776545.647 ops/s
# Warmup Iteration 9: 5698557.365 ops/s
# Warmup Iteration 10: 5408015.908 ops/s
# Warmup Iteration 11: 5369501.297 ops/s
# Warmup Iteration 12: 5656644.350 ops/s
# Warmup Iteration 13: 5927929.754 ops/s
# Warmup Iteration 14: 4925956.931 ops/s
# Warmup Iteration 15: 5073723.984 ops/s
# Warmup Iteration 16: 5562728.644 ops/s
# Warmup Iteration 17: 5404073.901 ops/s
# Warmup Iteration 18: 5710289.068 ops/s
# Warmup Iteration 19: 5279941.519 ops/s
# Warmup Iteration 20: 5313558.528 ops/s
Iteration 1: 5479700.075 ops/s
Iteration 2: 5435900.429 ops/s
Iteration 3: 5644384.753 ops/s
Iteration 4: 5439492.270 ops/s
Iteration 5: 4821232.721 ops/s
Iteration 6: 5255550.541 ops/s
Iteration 7: 5328415.572 ops/s
Iteration 8: 5303100.251 ops/s
Iteration 9: 5608949.378 ops/s
Iteration 10: 5493709.321 ops/s
Iteration 11: 5656755.883 ops/s
Iteration 12: 5342198.063 ops/s
Iteration 13: 5356092.929 ops/s
Iteration 14: 5448346.884 ops/s
Iteration 15: 5594615.720 ops/s
Iteration 16: 5263648.663 ops/s
Iteration 17: 5820217.743 ops/s
Iteration 18: 3766476.832 ops/s
Iteration 19: 5430792.407 ops/s
Iteration 20: 5607185.081 ops/s Result "com.jd.limitbuy.common.cache.offheap.local.localCacheBenchmark.testLocalCacheGet":
5354838.276 ±(99.9%) 371083.594 ops/s [Average]
(min, avg, max) = (3766476.832, 5354838.276, 5820217.743), stdev = 427340.418
CI (99.9%): [4983754.682, 5725921.870] (assumes normal distribution)
上面就是testLocalCacheGet方法的完整benchmark效果,我们可以看到起了4个线程,遍历了20次,每次都有一个ops。 最后的统计部分可以看到ops的具体值和偏差部分。可以说非常详尽。
当所有的方法都测算完毕之后,会汇总统计数据如下:
Benchmark Mode Cnt Score Error Units
localCacheBenchmark.testLocalCacheGet thrpt 20 5367451.445 ± 299325.857 ops/s
localCacheBenchmark.testLocalCacheHGet thrpt 20 1878476.142 ± 44977.163 ops/s
localCacheBenchmark.testLocalCacheHGetAll thrpt 20 2597442.245 ± 148661.259 ops/s
localCacheBenchmark.testLocalCacheHSet thrpt 20 39059.991 ± 60782.779 ops/s
localCacheBenchmark.testLocalCacheSAdd thrpt 20 231858.138 ± 80494.757 ops/s
localCacheBenchmark.testLocalCacheSet thrpt 20 168179.683 ± 145495.126 ops/s
localCacheBenchmark.testLocalCacheSmember thrpt 20 2650997.831 ± 97273.369 ops/s
localCacheBenchmark.testLocalCacheZAdd thrpt 20 56061.015 ± 73744.916 ops/s
localCacheBenchmark.testLocalCacheZRange thrpt 20 1682366.032 ± 75684.334 ops/s
这样我们就可以评估每个方法的ops性能了。
同样,如果想评估方法的tp50,tp99,tp999性能,只需要将BenchmarkMode改成Mode.AverageTime即可。非常方便。
注意,如果你使用idea,需要下载jmh-plugin插件支持。
基于JMH的Benchmark解决方案的更多相关文章
- 使用JMH做Benchmark基准测试
目录 BenchMark介绍 开始前的步骤 例子 代码 报告 注解介绍 @BenchmarkMode @OutputTimeUnit @Iteration @WarmUp @State @Fork @ ...
- 手淘架构组最新实践 | iOS基于静态库插桩的⼆进制重排启动优化 抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 编译期插桩
抖音研发实践:基于二进制文件重排的解决方案 APP启动速度提升超15% 原创 Leo 字节跳动技术团队 2019-08-09 https://mp.weixin.qq.com/s/Drmmx5JtjG ...
- 基于promtheus的监控解决方案
一.前言 鄙人就职于某安全公司,团队的定位是研发安全产品云汇聚平台,为用户提供弹性伸缩的云安全能力.前段时间产品组提出了一个监控需求,大致要求:平台对vm实行动态实时监控,输出相应图表界面,并提供警报 ...
- 搭建基于crtmpserver的点播解决方案
1. linux环境下build并启动crtmpserver 这部分可以参见我写的专项详解文章 <crtmpserver流媒体服务器的介绍与搭建> 和 <crtmpserver配置文 ...
- 基于openstack搭建百万级并发负载均衡器的解决方案
最近,喜欢研究一些国外技术大咖们的文章,而这篇文章是基于openstack负载均衡器的解决方案,做的一些总结~希望能够给小伙伴带来一些灵感或者帮助. openstack现有的负载均衡解决方案,无论是l ...
- CSS之基于不同场景的垂直居中解决方案
元素的水平居中,如果是一个行内元素,就对它的父元素应用 “text-align:center”: 如果是一个块级元素,就对它自身应用“margin:auto”. 垂直居中的几种场景以及实现方法: 一. ...
- JMH使用说明
JMH使用说明 一.概述 JMH,即Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件.何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测 ...
- 移动BPM解决方案分享
畅通开放 无边界的渠道 效率倍增 更高效的处理方式 即时共享 更强大的决策能力 各种终端应用 帮您实现:新任务通知.任务预警.催办.任务审批.任何数据汇总提醒消息通知...... 短信 客户端: ...
- 基于Xenomai的实时Linux分析与研究
转自:http://blog.csdn.net/cyberlabs/article/details/6967192 引 言 随着嵌入式设备的快速发展,嵌入式设备的功能和灵活性要求越来越高,很多嵌入式设 ...
随机推荐
- 【LeetCode每天一题】Length of Last Word(字符串中最后一个单词的长度)
Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the l ...
- Python中if __name__ == '__main__',__init__和self 的解析
1.2.1 一个.py文件被其他.py文件引用 假设我们有一个const.py文件,内容如下: 现在,我们写一个用于计算圆面积的area.py文件,area.py文件需要用到const.py文件中的P ...
- cocos creator 背景音乐音量和音效音量百分比设置
把音效的音量大小百分比保存在本地,播放音效的时候,带上音量大小,就像这样 你播放背景音乐时候,保存背景音乐的id,通过改变音量大小来调节背景音乐,就像这样 cc.audioEngine.setVolu ...
- sessionid固定与session混淆的一些随想
以前一直觉得sessionid固定和session混淆就是两个一样的东西,后来发现两者还是要分开来的,主要因为利用场景的不同!!! sessionid固定和session混淆还是需要区分开来的一般情况 ...
- ucli tcl cmd
ucli接口与tcl 8.6兼容:vcs中要调用ucli接口,执行脚本,必须在compile的时候,加入debug的权限: -debug,-debug_pp,-debug_all,-debug_acc ...
- Nginx技术研究系列4-Nginx监控-Nginx+Telegraf+Influxb+Grafana
搭建了Nginx集群后,需要继续深入研究的就是日常Nginx监控. Nginx如何监控?相信百度就可以找到:nginx-status 通过Nginx-status,实时获取到Nginx监控数据后,如何 ...
- OC OD介绍
参考:http://www.elecfans.com/baike/bandaoti/jichuzhishi/20100304178298.html OC门,又称集电极开路门,Open Collecto ...
- centos7使用yum安装软件提示 cannot find a valid baseurl for repo:base/7/x86_64 的解决方法
由于是本地yum源安装软件,无法联网,因此使用yum安装软件时报了错,解决方法是: 打开vi /etc/resolv.conf文件 新增内容如下: nameserver 8.8.8.8 nameser ...
- 自制操作系统Antz(4)——进入保护模式 (下) 实现内核并从硬盘载入
Antz系统更新地址: https://www.cnblogs.com/LexMoon/category/1262287.html Linux内核源码分析地址:https://www.cnblogs. ...
- 如何设置openwrt在编译linux内核时不优化内核?
答:修改openwrwt源码目录下rule.mk文件中的HOST_CFLAGS即可 将HOST_CFLAGS:=-O2 $(HOST_CPPFLAGS)改成: HOST_CFLAGS:=-O1 $(H ...