本文转载自微信公众号《粥雪》,作者帅小凡凡。转载本文请联系粥夏雪公众号。”小飞飞主管:“我们最近引入了mongodb,但是我们还没有实际测试过性能。刚听说读写比mysql快。你今天没有任何日程安排。测试一下,今天给我一个答案。”肖凡凡:“好吧,第二天就是测试性能了。此时,大多数人的第一个想法可能是直接使用这个方法:publicvoidtest(){longstart=System.currentTimeMillis();//执行逻辑longend=System.currentTimeMillis();System.out.println(end-start);}不,我这次用的是JMH。无论出于何种原因进行绩效评估,量化指标总是必不可少的,那么如何量化呢?这就需要我们的主角JMH出场了!先给大家介绍一下什么是JMH(JavaMicrobenchmarkHarness)JMH(JavaMicrobenchmarkHarness)是一个代码微基准测试的工具套件,主要基于方法级别的基准测试,精度可以达到纳秒级。这个工具是由Oracle内部JIT实现背后的人编写的,他们应该比任何人都更了解JIT和JVM对基准测试的影响。当你定位到一个热点方法,想进一步优化该方法的性能时,可以使用JMH对优化结果进行定量分析。JMH的典型应用场景如下:想知道一个方法到底执行了多长时间,以及执行时间和输入的相关性比较给定条件下不同接口实现的吞吐量查看请求在多少中完成的百分比很长一段时间我们以mongodb、hibernate、jdbc数据加载性能对比为例,使用JMH进行benchmarking。如何做JMH基准测试?添加依赖,因为JMH是JDK9自带的。如果是JDK9之前的版本,需要添加如下依赖:org.openjdk.jmhjmh-core1.29org.openjdk.jmhjmh-generator-annprocess1.29写一个benchmark测试接下来创建一个JMH测试类,具体代码如下:@BenchmarkMode({Mode.AverageTime})@Warmup(iterations=1,time=5)@Measurement(iterations=3,time=5)@Threads(1)@Fork(1)@OutputTimeUnit(TimeUnit.MILLISECONDS)@State(Scope.Benchmark)publicclassReadBenchMarks{@BenchmarkpublicvoidloadMongoTemplate(){//mongoTemplate数据加载}@BenchmarkpublicvoidloadMongoDriver(){//mongoDriver数据加载}@BenchmarkpublicvoidloadHibernate(){//hibernate数据加载}@BenchmarkpublicvoidloadJdbc(){//jdbc数据加载}publicstaticvoidmain(String[]args)throwsRunnerException{Optionsoptions=newOptionsBuilder().include(ReadBenchMarks.class.getSimpleName()).output("db.log").build();newRunner(options).run();}}《核心关注:》注解需要测试的方法使用了@具体下面将介绍这些注解的含义有兴趣的可以看看jmh官方的sampledemo:http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/执行基准测试的准备工作就绪。接下来运行代码,稍等片刻,测试结果就出来了。#JMHversion:1.29#VMversion:JDK1.8.0_251,JavaHotSpot(TM)ClientVM,25.251-b08#VMinvoker:C:\soft\Java\jdk1.8.0_251\jre\bin\java.exe#VMoptions:-javaagent:C:\soft\idea\IntelliJIDEACommunityEdition2020.1.1\lib\idea_rt.jar=53895:C:\soft\idea\IntelliJIDEACommunityEdition2020.1.1\bin-Dfile.encoding=UTF-8#Blackholemode:full+dont-inlinehint#Warmup:2iterations,5seach#Measurement:10iterations,5seach#Timeout:10minperiteration#Threads:1thread,willsynchronizeiterations#Benchmarkmode:Averagetime,time/op#Benchmark:com.db.jmh.write.WriteBenchMarks.writeHibernate#Parameters:(info=10031,1,5)#Runprogress:0.00%complete,ETA00:06:00#Fork:1of1#WarmupIteration1:7.743ms/op#WarmupIteration2:9.433ms/opIteration1:7.854ms/opIteration2:8.638ms/opIteration3:8.579ms/opIteration4:8.213ms/opIteration5:8.843ms/opIterationion6:9.178ms/opIteration7:7.739ms/opIteration8:9.608ms/opIteration9:10.152ms/opIteration10:9.461ms/opResult"com.db.jmh.write.WriteBenchMarks.writeHibernate":8.827±(99.9%)1.182ms/op[平均值](min,avg,max)=(7.739,8.827,10.152),stdev=0.782CI(99.9%):[7.645,10.008](assumesnormaldistribution)#Runcomplete.Totaltime:00:06:38REMEMBER:Thenumbersbelowarejustdata.Tagainreusableinsights,youneedtofollowuponwhythenumbersarethewaytheyare.Useprofilers(see-prof,-lprof),designfactorialexperiments,performbaselineandnegativeteststhatprovideexperimentalcontrol,makesurethebenchmarkingenvironmentissafeonJVM/OS/HWlevel,askforreviewsfromthedomainexperts.Donotassumethenumberstellyouwhatyouwantthemtotell.Benchmark(info)ModeCntScoreErrorUnitsWriteBenchMarks.writeHibernate10031,1,5avgt108.827±1.182ms/opWriteBenchMarks.writeHibernate10032,5,6avgt108.783±1.478ms/opWriteBenchMarks.writeHibernate10033,5,20avgt1012.574±0.928ms/opWriteBenchMarks.writeMongo10031,1,5avgt105.057±0.358ms/opWriteBenchMarks.writeMongo10032,5,6avgt107.392±0.651ms/opWriteBenchMarks.writeMongo10033,5,20avgt1012.590±0.795ms/op:下面是对结果的简单描述#JMversion:1.29#VMversion:JDK1.8.0_251,JavaHotSpot(TM)ClientVM,25.251-b08#VMinvoker:C:\soft\Java\jdk1.8.0_251\jre\bin\java.exe#VMoptions:-javaagent:C:\soft\idea\IntelliJIDEACommunityEdition2020.1.1\lib\idea_rt.jar=53895:C:\soft\idea\IntelliJIDEACommunityEdition2020.1.1\bin-Dfile.encoding=UTF-8#Blackholemode:full+dont-inlinehint#Warmup:2iterations,5seach#Measurement:10iterations,5seach#Timeout:10minperiteration#Threads:1thread,willsynchronizeiterations#Benchmarkmode:Averagetime,time/op#Benchmark:com.db.jmh.write.WriteBenchMarks.writeHibernate#Parameters:(信息=10031,1,5)这部分是“测试的基本信息”,比如使用的Java路径,预热代码的迭代次数,测量代码的迭代次数,使用的线程数,统计uni测试的t等等#WarmupIteration1:7.743ms/op#WarmupIteration2:9.433ms/op部分是每次warmup中的性能指标,warmup测试不会作为最终的统计结果。预热的目的是“让JVM对被测代码进行足够的优化”,比如预热之后,对被测代码进行充分的JIT编译和优化。Iteration1:7.854ms/opIteration2:8.638ms/opIteration3:8.579ms/opIteration4:8.213ms/opIteration5:8.843ms/opIteration6:9.178ms/opIteration7:7.739ms/opIteration8:9.608ms/opIteration9:10.152ms/opIteration10:9。opResult"com.db.jmh.write.WriteBenchMarks.writeHibernate":8.827±(99.9%)1.182ms/op[Average](min,avg,max)=(7.739,8.827,10.152),stdev=0.782CI(99.9%):[7.645,10.008](assumesnormaldistribution)这部分显示了测量迭代的情况。每次迭代显示当前执行率,即操作所花费的时间。迭代10次后,进行统计。最后最后的:基准(INFO)MODECNTSCOREERRORROUNITSWRITEBENCHS.WRITEHIBERNATE10031,1,1,5AVGT108.827±1.182MS/OPWRITEBENCHS.WRITEHIBERNATE10032,5,10032,10032,1RMARKENNE/opWriteBenchMarks.writeMongo10031,1,5avgt105.057±0.358ms/opWriteBenchMarks.writeMongo10032,5,6avgt107.392±0.651ms/opWriteBenchMarks.writeMongo10033,5,20avgt1012.590±0.795ms/op,但我不太可能抛出这个数据直接给boss,所以我用了下面两个网站JMHVisualChart:http://deepoove.com/jmh-visual-chart/JMHVisualizer:https://jmh.morethan。io/生成一开始看到的图形界面。另外,为了更好的使用JMH的功能,下面对JMH的基本概念进行说明:@BenchmarkMode用于配置Mode选项,可以用在类或方法上。这个注解的值是一个数组。几个Modes可以一起执行,比如:@BenchmarkMode({Mode.SampleTime,Mode.AverageTime}),也可以设置为Mode.All,即全部执行一次。Throughput:整体吞吐量,每秒执行多少次调用,单位为ops/timeAverageTime:平均使用时间,每次操作的平均时间,单位为time/opSampleTime:随机采样,最终输出采样结果的分布SingleShotTime:只运行一次,经常同时设置Warmups的个数为0来测试冷启动时的性能按范围和份额经营。@State可以被继承和使用。如果父类定义了注解,则子类不需要定义。由于JMH允许多个线程同时执行测试,所以不同选项的含义如下:Scope.Benchmark:所有测试线程共享一个实例,测试多线程共享下有状态实例的性能Scope.Group:该同一个线程在同一个组中共享InstanceScope.Thread:默认状态,每个测试线程分配一个实例@OutputTimeUnit作为统计结果的时间单位,可以用于类或者方法注解@Warmup的一些基本测试参数需要配置预热可用于类或方法。一般前几次程序测试会比较慢,所以让程序进行几轮预热,以保证测试的准确性。参数如下:iterations:预热次数time:每次预热的时间timeUnit:时间单位,默认秒基本测试参数,可以用在类或方法上,参数与@Warmup相同。@Threads每个进程中的测试线程,可以用在类或方法上。@Forkfork次数,可以用在类或方法上。如果fork数为2,JMH会fork两个进程进行测试。@Param指定了某个参数的多种情况,特别适用于测试一个函数在不同参数输入条件下的性能。它只能用于字段。要使用此注释,您必须定义@State注释。介绍完常用的注解,再看看JMH都有哪些坑。回答一个问题,为什么需要热身?由于JVM的JIT机制的存在,如果一个函数被多次调用,JVM会尝试将其编译成机器码来提高执行速度,所以为了让benchmark的结果更接近真实情况需要被热身。如何可视化测试结果其实很简单。将主要功能更改为publicstaticvoidmain(String[]args)throwsRunnerException{Optionsopt=newOptionsBuilder().include(WriteBenchMarks.class.getSimpleName()).result("db_read.json").resultFormat(ResultFormatType.JSON).build();newRunner(opt).run();}就可以了,然后把生成的json格式文件扔到下面网址:JMHVisualChart:http://deepoove.com/jmh-visual-chart/JMHVisualizer:https://jmh.morethan.io/就可以了。”小凡凡:“我测试完了,还生成了直方图给你看。()这种低级手段,没想到用到了JMH,干得不错。工资马上就要涨了,我必须给你加一笔钱。原文链接:https://mp.weixin.qq.com/s/hTRA-eOSvSns0sm2P2BMVg