暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

高性能工具:JMH 微基准测试

搬运工来架构 2018-12-06
674

一、简介 

    JMH (Java Microbenchmark Harness)是一个用于构建、运行和分析用Java和其他针对JVM的语言编写的nano/micro/milli/macro基准测试的Java工具。OpenJDK提供的基准测试工具,是由Oracle实现JIT的相同人员开发的。

    在开发过程中,为了确定一个方法的性能,很多时候都是写一个循环多次调用方法通过计算时间来查看性能是否达到预期。但这种简单的方式得出的结论往往是不准确的。现代JVM不断优化,随着代码执行次数的增加,JVM会对其进行编译优化(JIT),最终结果与简单测试得出的结果可能差距很大。此外,测试往往需要做很多额外工作,比如:控制读写线程比例,控制线程同时启动,设置多线程测试中的状态对象,统计测试结果等等,自己实现这些功能既繁琐又不能完全保证正确。而JMH就是恰好能满足上述性能测试需求的工具。

    在代码进行微基准测试时必备的工具利器,在大部分优秀的框架(Netty等)都能看到针对代码性能的测试例子。或者你写了几种代码方案,但是不确定哪种方式是性能最优的,这时你可以使用JMH进行压测,就能知道哪种方案是最优的了。所以,了解和学习JMH对你是有一定的帮助的。

    

二、JMH常用的场景:

    1、想准确的知道某个方法需要执行多长时间,以及执行时间和输入之间的相关性;

    2、对比接口不同实现在给定条件下的吞吐量;

    3、查看多少百分比的请求在多长时间内完成。

    等等。


三、使用

    目前,最新的版本是1.21。推荐使用maven的方式导入相关的依赖包:

 1       <dependency>
2            <groupId>org.openjdk.jmh</groupId>
3            <artifactId>jmh-core</artifactId>
4            <version>1.21</version>
5            <scope>test</scope>
6        </dependency>
7        <dependency>
8            <groupId>org.openjdk.jmh</groupId>
9            <artifactId>jmh-generator-annprocess</artifactId>
10            <version>1.21</version>
11            <scope>test</scope>
12        </dependency>


    Demo:

 1import org.openjdk.jmh.annotations.Benchmark;
2import org.openjdk.jmh.annotations.BenchmarkMode;
3import org.openjdk.jmh.annotations.Mode;
4import org.openjdk.jmh.annotations.OutputTimeUnit;
5import org.openjdk.jmh.runner.Runner;
6import org.openjdk.jmh.runner.RunnerException;
7import org.openjdk.jmh.runner.options.Options;
8import org.openjdk.jmh.runner.options.OptionsBuilder;
9
10import java.util.concurrent.TimeUnit;
11
12/**
13 * String、StringBuilder、StringBuffer微基准测试
14 * @author Cocodroid
15 * @create 2018-12-02 20:56
16 */

17@BenchmarkMode(Mode.Throughput)
18@OutputTimeUnit(TimeUnit.MILLISECONDS)
19public class JMHStringTest {
20    @Benchmark
21    public String testString() {
22        String a = "";
23        for (int i = 0; i < 100; i++) {
24            a += i;
25        }
26        return a;
27    }
28
29    @Benchmark
30    public String testStringBuilder() {
31        StringBuilder sb = new StringBuilder();
32        for (int i = 0; i < 100; i++) {
33            sb.append(i);
34        }
35        return sb.toString();
36    }
37
38    @Benchmark
39    public String testStringBuffer() {
40        StringBuffer sb = new StringBuffer();
41        for (int i = 0; i < 100; i++) {
42            sb.append(i);
43        }
44        return sb.toString();
45    }
46
47    public static void main(String[] args) throws RunnerException {
48        Options opt = new OptionsBuilder()
49                .include(JMHStringTest.class.getSimpleName())
50                .forks(1) 进程数
51                .warmupIterations(3) 预热迭代次数
52                .measurementIterations(5) 度量测试迭代次数
53                .build();
54        new Runner(opt).run();
55    }
56
57}

    

    结果:

  1# JMH version: 1.21
 2# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
 3# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
 4# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar
=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
 5# Warmup: 3 iterations, 10 s each
 6# Measurement: 5 iterations, 10 s each
 7# Timeout: 10 min per iteration
 8# Threads: 1 thread, will synchronize iterations
 9# Benchmark mode: Throughput, ops/time
10# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testString
11
12# Run progress: 0.00% complete, ETA 00:04:00
13# Fork: 1 of 1
14# Warmup Iteration   1246.668 ops/ms
15# Warmup Iteration   2288.384 ops/ms
16# Warmup Iteration   3290.934 ops/ms
17Iteration   1290.999 ops/ms
18Iteration   2291.096 ops/ms
19Iteration   3198.395 ops/ms
20Iteration   4290.480 ops/ms
21Iteration   5289.623 ops/ms
22
23
24Result "com.verysu.study.test.jmh.JMHStringTest.testString":
25  272.119 ±(99.9%) 158.711 ops/ms [Average]
26  (min, avg, max) = (198.395272.119291.096), stdev = 41.217
27  CI (99.9%): [113.407430.830] (assumes normal distribution)
28
29
30# JMH version: 1.21
31# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
32# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
33# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar
=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
34# Warmup: 3 iterations, 10 s each
35# Measurement: 5 iterations, 10 s each
36# Timeout: 10 min per iteration
37# Threads: 1 thread, will synchronize iterations
38# Benchmark mode: Throughput, ops/time
39# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testStringBuffer
40
41# Run progress: 33.33% complete, ETA 00:02:43
42# Fork: 1 of 1
43# Warmup Iteration   1451.843 ops/ms
44# Warmup Iteration   2571.156 ops/ms
45# Warmup Iteration   3594.271 ops/ms
46Iteration   1594.787 ops/ms
47Iteration   2594.936 ops/ms
48Iteration   3587.871 ops/ms
49Iteration   4468.530 ops/ms
50Iteration   5546.836 ops/ms
51
52
53Result "com.verysu.study.test.jmh.JMHStringTest.testStringBuffer":
54  558.592 ±(99.9%) 208.590 ops/ms [Average]
55  (min, avg, max) = (468.530558.592594.936), stdev = 54.170
56  CI (99.9%): [350.002767.182] (assumes normal distribution)
57
58
59# JMH version: 1.21
60# VM version: JDK 1.8.0_131, Java HotSpot(TM) 64-Bit Server VM, 25.131-b11
61# VM invoker: D:\InstallSoft\Java\jdk1.8.0_131\jre\bin\java.exe
62# VM options: -javaagent:D:\InstallSoft\ideaIU-2017.1.4.win\lib\idea_rt.jar
=54475:D:\InstallSoft\ideaIU-2017.1.4.win\bin -Dfile.encoding=UTF-8
63# Warmup: 3 iterations, 10 s each
64# Measurement: 5 iterations, 10 s each
65# Timeout: 10 min per iteration
66# Threads: 1 thread, will synchronize iterations
67# Benchmark mode: Throughput, ops/time
68# Benchmark: com.verysu.study.test.jmh.JMHStringTest.testStringBuilder
69
70# Run progress: 66.67% complete, ETA 00:01:21
71# Fork: 1 of 1
72# Warmup Iteration   1838.055 ops/ms
73# Warmup Iteration   2848.125 ops/ms
74# Warmup Iteration   3684.022 ops/ms
75Iteration   1510.202 ops/ms
76Iteration   2736.051 ops/ms
77Iteration   3713.015 ops/ms
78Iteration   4841.232 ops/ms
79Iteration   5842.549 ops/ms
80
81
82Result "com.verysu.study.test.jmh.JMHStringTest.testStringBuilder":
83  728.610 ±(99.9%) 522.563 ops/ms [Average]
84  (min, avg, max) = (510.202728.610842.549), stdev = 135.708
85  CI (99.9%): [206.0471251.172] (assumes normal distribution)
86
87
88# Run complete. Total time: 00:04:03
89
90REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
91why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
92experiments, perform baseline and negative tests that provide experimental control, make sure
93the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
94Do not assume the numbers tell you what you want them to tell.
95
96Benchmark                         Mode  Cnt    Score     Error   Units
97JMHStringTest.testString         thrpt    5  272.119 ± 158.711  ops/ms
98JMHStringTest.testStringBuffer   thrpt    5  558.592 ± 208.590  ops/ms
99JMHStringTest.testStringBuilder  thrpt    5  728.610 ± 522.563  ops/ms
100


    

预热迭代3次,度量测试迭代5次,进行100次字符串的拼接操作。

从上面的测试结果来看,跟我们之前学习String、StringBuilder、StringBuffer对字符串的拼接操作,预测的性能结果很吻合:(吞吐量)StringBuilder(728) > StringBuffer(558) > String(272)。所以,你知道JMH的作用和威力了吧?


四、常用注解

    1、@Benchmark 

        标记要测试的方法。


    2、@BenchmarkMode

        设置测试模式,Mode.Throughput:吞吐量模式,一个时间单位内操作数(即是本文上面的例子);Mode.AverageTime:平均执行时间模式;Mode.SampleTime:采样模式;Mode.SingleShotTime:单次操作模式;Model.All:包含上面的全部模式。


    3、@OutputTimeUnit

        输出结果的时间粒度,有秒、毫秒、微妙、纳秒等时间单位。


    4、@State

    Scope.Thread:默认的State,每个测试线程分配一个实例;

    Scope.Benchmark:所有测试线程共享一个实例,用于测试有状态实例在多线程共享下的性能;

    Scope.Group:每个线程组共享一个实例。


    5、@Fork

        设置进程数,以及JVM参数。


五、资料

     学习一手jmh资料还是要关注官网,官网给出的几十个例子都是非常不错的资料。

    JMH官网地址:

        http://openjdk.java.net/projects/code-tools/jmh/

    JMH官网38个例子

        http://hg.openjdk.java.net/code-tools/jmh/file/tip/jmh-samples/src/main/java/org/openjdk/jmh/samples/

    相信上面的资料文档都能给你很多帮助!

    

参考资料:

    https://blog.csdn.net/lxbjkben/article/details/79410740

    https://www.jianshu.com/p/d2517aaced40

    https://mp.weixin.qq.com/s/uIhbLSlZIPypy-rs5GZndg


温馨提示

若觉得这篇文章不错,请分享到朋友圈。

更多干货,关注公众号:搬运工来架构。

搬运工来架构

挨踢人聚集地,一个分享干货的公众号

作者:cocodroid

来源:https://blog.verysu.com/article/411


你可能喜欢


文章转载自搬运工来架构,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论