JMH简介
官网:http://openjdk.java.net/projects/code-tools/
github:https://github.com/openjdk/jmh
简介:JMH is a Java harness for building, running, and analysing nano/micro/milli/macro benchmarks written in Java and other languages targetting the JVM,
由简介可知,JMH不止能对Java语言做基准测试,还能对运行在JVM上的其他语言做基准测试。而且可以分析到纳秒级别。
推荐用法
官方推荐创建一个独立的Maven工程来运行JMH基准测试,这样更能确保结果的准确性。当然也可以在已存在的工程中,或者在IDE上运行,但是越复杂,结果越不可靠(more complex and the results are less reliable)。
简单实用
推荐用法通过命令行创建,构建和运行JMH基准测试。
setup1
生成一个新的JMH工程的maven命令如下:
$ mvn archetype:generate \
-DinteractiveMode=false \
-DarchetypeGroupId=org.openjdk.jmh \
-DarchetypeArtifactId=jmh-java-benchmark-archetype \
-DgroupId=org.sample \
-DartifactId=test \
-Dversion=1.0
setup2
建立基准。生成项目后,可以使用以下Maven命令来构建它:
$ cd test/
$ mvn clean package
setup3
运行基准测试。构建完成后,您将获得独立的可执行文件JAR(该文件将保存您的基准测试)以及所有必要的JMH基础结构代码:
$ java -jar target/benchmarks.jar
压测代码
这里我测试一下cglib、orika、spring等bean copy工具的性能
在上面生成的JMH工程里面的pom.xml引入依赖:
<!-- spring-beans -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<!-- commons-beanutils -->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<!-- orika -->
<dependency>
<groupId>ma.glasnost.orika</groupId>
<artifactId>orika-core</artifactId>
<version>1.5.4</version>
</dependency>
测试类:
package org.sample;
import ma.glasnost.orika.MapperFacade;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import net.sf.cglib.beans.BeanCopier;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.sample.other.User;
import org.sample.other.UserService;
import org.sample.other.UserVO;
import java.util.concurrent.TimeUnit;
/**
* 结果如下,重点关注Mean和Units两个字段,组合起来是1227.928ns/op,即每次操作耗时1227.928纳秒:Mean error表示误差,或者波动
* Benchmark Mode Samples Mean Mean error Units
* o.s.MyBenchmark.testApacheBeanUtils avgt 5 306706.144 46350.093 ns/op
* o.s.MyBenchmark.testCglibBeanCopier avgt 5 159.403 3.243 ns/op
* o.s.MyBenchmark.testDeadCode avgt 5 99.916 2.527 ns/op
* o.s.MyBenchmark.testOrikaBeanCopy avgt 5 825.971 15.891 ns/op
* o.s.MyBenchmark.testSpringBeanUtils avgt 5 12034.272 307.476 ns/op
*
* 如果我们将@BenchmarkMode(Mode.AverageTime)与@OutputTimeUnit(TimeUnit.NANOSECONDS)的组合,改成@BenchmarkMode(Mode.Throughput)和@OutputTimeUnit(TimeUnit.MILLISECONDS),那么基准测试结果就是每毫秒的吞吐量(即每毫秒多少次操作),结果如下,表示943.437ops/ms:
*
*/
/**
* 基准测试后对代码预热总计5秒(迭代5次,每次1秒)。预热对于压测来说非常非常重要,如果没有预热过程,压测结果会很不准确。
* # Warmup Iteration 2: 13349.349 ns/op
* # Warmup Iteration 3: 12084.938 ns/op
* # Warmup Iteration 4: 12032.203 ns/op
* # Warmup Iteration 5: 12127.331 ns/op
*/
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
/**
* 循环运行5次,每次迭代时间为1秒,总计5秒时间。
*/
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
/**
* 表示fork多少个线程运行基准测试,如果@Fork(1),那么就是一个线程,这时候就是同步模式。
*/
@Fork(1)
/**
* 基准测试模式,@BenchmarkMode(Mode.AverageTime)搭配@OutputTimeUnit(TimeUnit.NANOSECONDS)
* 表示每次操作需要的平均时间,而OutputTimeUnit申明为纳秒,所以基准测试单位是ns/op,即每次操作的纳秒单位平均时间
*/
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {
/**
* 被拷贝的源对象
*/
@State(Scope.Benchmark)
public static class CommonState {
User user;
@Setup(Level.Trial)
public void prepare() {
user = new UserService().get();
}
}
/**
* 测试手动getter/setter拷贝对象属性
* @param commonState
* @return UserVO
* @throws Exception
*/
@GenerateMicroBenchmark
public UserVO testDeadCode(MyBenchmark.CommonState commonState) throws Exception {
UserVO userVO = new UserVO();
User user = commonState.user;
userVO.setAccount(user.getAccount());
userVO.setAddress(user.getAddress());
userVO.setAge(user.getAge());
userVO.setBirthday(user.getBirthday());
userVO.setDepartment(user.getDepartment());
userVO.setDiploma(user.getDiploma());
userVO.setEmail(user.getEmail());
userVO.setFax(user.getFax());
userVO.setId(user.getId());
userVO.setIdCard(user.getIdCard());
userVO.setInnerTel(user.getInnerTel());
userVO.setJob(user.getJob());
userVO.setJoinDate(user.getJoinDate());
userVO.setLeaved(user.getLeaved());
userVO.setLoginDate(user.getLoginDate());
userVO.setMaritalStatus(user.getMaritalStatus());
userVO.setMobile(user.getMobile());
userVO.setName(user.getName());
userVO.setNo(user.getNo());
userVO.setOuterTel(user.getOuterTel());
userVO.setPassword(user.getPassword());
userVO.setPermanentAddress(user.getPermanentAddress());
userVO.setPicture(user.getPicture());
userVO.setPinyin(user.getPinyin());
userVO.setPosition(user.getPosition());
userVO.setQq(user.getQq());
userVO.setSex(user.getSex());
userVO.setStatus(user.getStatus());
userVO.setType(user.getType());
userVO.setWeixin(user.getWeixin());
userVO.setWeixinQrcode(user.getWeixinQrcode());
userVO.setField00(user.getField00());
userVO.setField01(user.getField01());
userVO.setField02(user.getField02());
userVO.setField03(user.getField03());
userVO.setField04(user.getField04());
userVO.setField05(user.getField05());
userVO.setField06(user.getField06());
userVO.setField07(user.getField07());
userVO.setField08(user.getField08());
userVO.setField09(user.getField09());
userVO.setField10(user.getField10());
userVO.setField11(user.getField11());
userVO.setField12(user.getField12());
userVO.setField13(user.getField13());
userVO.setField14(user.getField14());
userVO.setField15(user.getField15());
userVO.setField16(user.getField16());
userVO.setField17(user.getField17());
userVO.setField18(user.getField18());
userVO.setField19(user.getField19());
userVO.setField20(user.getField20());
userVO.setField21(user.getField21());
userVO.setField22(user.getField22());
userVO.setField23(user.getField23());
userVO.setField24(user.getField24());
userVO.setField25(user.getField25());
userVO.setField26(user.getField26());
userVO.setField27(user.getField27());
userVO.setField28(user.getField28());
userVO.setField29(user.getField29());
userVO.setField30(user.getField30());
userVO.setField31(user.getField31());
userVO.setField32(user.getField32());
userVO.setField33(user.getField33());
userVO.setField34(user.getField34());
userVO.setField35(user.getField35());
userVO.setField36(user.getField36());
userVO.setField37(user.getField37());
userVO.setField38(user.getField38());
userVO.setField39(user.getField39());
userVO.setField40(user.getField40());
userVO.setField41(user.getField41());
userVO.setField42(user.getField42());
userVO.setField43(user.getField43());
userVO.setField44(user.getField44());
userVO.setField45(user.getField45());
userVO.setField46(user.getField46());
userVO.setField47(user.getField47());
userVO.setField48(user.getField48());
userVO.setField49(user.getField49());
userVO.setField50(user.getField50());
userVO.setField51(user.getField51());
userVO.setField52(user.getField52());
userVO.setField53(user.getField53());
userVO.setField54(user.getField54());
userVO.setField55(user.getField55());
userVO.setField56(user.getField56());
userVO.setField57(user.getField57());
userVO.setField58(user.getField58());
userVO.setField59(user.getField59());
userVO.setField60(user.getField60());
userVO.setField61(user.getField61());
userVO.setField62(user.getField62());
userVO.setField63(user.getField63());
userVO.setField64(user.getField64());
userVO.setField65(user.getField65());
userVO.setField66(user.getField66());
userVO.setField67(user.getField67());
userVO.setField68(user.getField68());
userVO.setField69(user.getField69());
userVO.setField70(user.getField70());
userVO.setField71(user.getField71());
userVO.setField72(user.getField72());
userVO.setField73(user.getField73());
userVO.setField74(user.getField74());
userVO.setField75(user.getField75());
userVO.setField76(user.getField76());
userVO.setField77(user.getField77());
userVO.setField78(user.getField78());
userVO.setField79(user.getField79());
userVO.setField80(user.getField80());
userVO.setField81(user.getField81());
userVO.setField82(user.getField82());
userVO.setField83(user.getField83());
userVO.setField84(user.getField84());
userVO.setField85(user.getField85());
userVO.setField86(user.getField86());
userVO.setField87(user.getField87());
userVO.setField88(user.getField88());
userVO.setField89(user.getField89());
userVO.setField90(user.getField90());
userVO.setField91(user.getField91());
userVO.setField92(user.getField92());
userVO.setField93(user.getField93());
userVO.setField94(user.getField94());
userVO.setField95(user.getField95());
userVO.setField96(user.getField96());
userVO.setField97(user.getField97());
userVO.setField98(user.getField98());
userVO.setField99(user.getField99());
assert "zzs0".equals(userVO.getName());
return userVO;
}
/**
* 测试apache BeanUtils拷贝对象属性
* @param commonState
* @return UserVO
* @throws Exception
*/
@GenerateMicroBenchmark
public UserVO testApacheBeanUtils(MyBenchmark.CommonState commonState) throws Exception {
UserVO userVO = new UserVO();
org.apache.commons.beanutils.BeanUtils.copyProperties(userVO, commonState.user);
assert "zzs0".equals(userVO.getName());
return userVO;
}
/**
* 测试spring BeanUtils拷贝对象属性
* @param commonState
* @return UserVO
* @throws Exception
*/
@GenerateMicroBenchmark
public UserVO testSpringBeanUtils(MyBenchmark.CommonState commonState) throws Exception {
UserVO userVO = new UserVO();
org.springframework.beans.BeanUtils.copyProperties(commonState.user, userVO);
assert "zzs0".equals(userVO.getName());
return userVO;
}
/**
* 测试cglib BeanCopier拷贝对象属性
* @param commonState
* @return UserVO
* @throws Exception
*/
@GenerateMicroBenchmark
public UserVO testCglibBeanCopier(MyBenchmark.CommonState commonState) throws Exception {
BeanCopier copier = BeanCopier.create(commonState.user.getClass(), UserVO.class, false);
UserVO userVO = new UserVO();
copier.copy(commonState.user, userVO, null);
assert "zzs0".equals(userVO.getName());
return userVO;
}
/**
* 测试使用orika拷贝对象属性
* @param commonState
* @return UserVO
* @throws Exception
*/
@GenerateMicroBenchmark
public UserVO testOrikaBeanCopy(MyBenchmark.CommonState commonState, MyBenchmark.OrikaState orikaState) throws Exception {
MapperFacade mapperFacade = orikaState.mapperFactory.getMapperFacade();
UserVO userVO = mapperFacade.map(commonState.user, UserVO.class);
assert "zzs0".equals(userVO.getName());
return userVO;
}
@State(Scope.Benchmark)
public static class OrikaState {
MapperFactory mapperFactory;
@Setup(Level.Trial)
public void prepare() {
mapperFactory = new DefaultMapperFactory.Builder().build();
}
}
这里省略了一些实体类,测试类里面有对于注解的注释。
写完测试类执行mvn clean package 打包
然后执行java -jar target/benchmarks.jar
得到结果:
Benchmark Mode Samples Mean Mean error Units
o.s.MyBenchmark.testApacheBeanUtils avgt 5 306706.144 46350.093 ns/op
o.s.MyBenchmark.testCglibBeanCopier avgt 5 159.403 3.243 ns/op
o.s.MyBenchmark.testDeadCode avgt 5 99.916 2.527 ns/op
o.s.MyBenchmark.testOrikaBeanCopy avgt 5 825.971 15.891 ns/op
o.s.MyBenchmark.testSpringBeanUtils avgt 5 12034.272 307.476 ns/op
根据测试结果,对象拷贝速度方面:
手动拷贝 > cglib beanCopier > orika mapper > spring beanUtils > apache commons beanUtils