前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微基准测试框架JMH

微基准测试框架JMH

作者头像
李鸿坤
发布2020-07-17 14:02:16
5640
发布2020-07-17 14:02:16
举报
文章被收录于专栏:泛泛聊后端

最典型的场景就是你想知道两个功能相同的操作到底哪个性能比较好,通常会自己手撸一段代码,前后增加时间,然后对比多次执行的时间。这种做法比较原始,还要自己处理预热等问题。JMH提供了比较丰富的操作。且看如何使用。

使用示例

这个工具是JDK9 加入的,但是也可以直接使用Maven添加对应的依赖。

代码语言:javascript
复制
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>${jmh.version}</version>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>${jmh.version}</version>
    <scope>provided</scope>
</dependency>

依赖添加完成以后即可编写性能测试程序

代码语言:javascript
复制
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
public class JmhApplication {

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder()
                .include(JmhApplication.class.getSimpleName())
                .forks(1)
                .warmupIterations(2)
                .measurementIterations(5)
                .build();

        new Runner(opt).run();
    }

    @Benchmark
    public void stringAdd(){
        String a = "";
        for (int i = 0; i < 10; i++) {
            a += i;
        }
    }

    @Benchmark
    public void stringBuilderAdd(){
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 10; i++) {
            sb.append(i);
        }
    }
}

如上述代码只需要在要进行基准测试的方法上增加Benchmark注解,即可进行基准测试。而类上的注解直接定义了基准测试的一些全局设置,如测试类型,时间单位等。

main方法里面使用Options类定义的预热运行测试方法的次数,以及运行基准测试运行方法的次数。接着构建Runner类运行基准测试。

代码语言:javascript
复制
// .... 省略其他输出
# Run progress: 50.00% complete, ETA 00:01:11
# Fork: 1 of 1
# Warmup Iteration   1: 23.463 ops/us
# Warmup Iteration   2: 19.687 ops/us
Iteration   1: 18.304 ops/us
Iteration   2: 20.687 ops/us
Iteration   3: 20.641 ops/us
Iteration   4: 20.194 ops/us
Iteration   5: 20.112 ops/us
// .... 省略其他输出
Benchmark                         Mode  Cnt   Score   Error   Units
JmhApplication.stringAdd         thrpt    5   5.711 ± 1.784  ops/us
JmhApplication.stringBuilderAdd  thrpt    5  19.988 ± 3.757  ops/us

结果如上,可以看出进行了2次的预热,执行5次迭代,来计算结果。最终输出了Benchmark的结果。关注点是Score和对应的单位Units,表示每个时间单位执行的操作次数。即第一个方法每微秒执行5.711 ± 1.784次,第二个方法是每微秒执行19.988 ± 3.757 次。

基准测试类型

基准测试类型,由注解BenchmarkMode来指定,主要由五种选择,实际是4种。

Throughput 整体吞吐量,结果是单位时间的执行次数。

代码语言:javascript
复制
Benchmark                         Mode  Cnt   Score   Error   Units
JmhApplication.stringAdd         thrpt    5   5.711 ± 1.784  ops/us
JmhApplication.stringBuilderAdd  thrpt    5  19.988 ± 3.757  ops/us

AverageTime 调用的平均时间,结果是执行一次所需要的时间。

代码语言:javascript
复制
Benchmark                        Mode  Cnt  Score   Error  Units
JmhApplication.stringAdd         avgt    5  0.150 ± 0.002  us/op
JmhApplication.stringBuilderAdd  avgt    5  0.049 ± 0.005  us/op

SampleTime 随机取样,最后输出取样结果的分布

代码语言:javascript
复制
Benchmark                                  Mode      Cnt     Score   Error  Units
stringAdd                                  sample  1264158     0.202 ± 0.019  us/op
stringAdd:stringAdd·p0.00                  sample              0.100          us/op
stringAdd:stringAdd·p0.50                  sample              0.200          us/op
stringAdd:stringAdd·p0.90                  sample              0.200          us/op
stringAdd:stringAdd·p0.95                  sample              0.200          us/op
stringAdd:stringAdd·p0.99                  sample              0.300          us/op
stringAdd:stringAdd·p0.999                 sample              2.200          us/op
stringAdd:stringAdd·p0.9999                sample             31.907          us/op
stringAdd:stringAdd·p1.00                  sample           3895.296          us/op
stringBuilderAdd                           sample  1968851     0.082 ± 0.008  us/op
stringBuilderAdd:stringBuilderAdd·p0.00    sample                ≈ 0          us/op
stringBuilderAdd:stringBuilderAdd·p0.50    sample              0.100          us/op
stringBuilderAdd:stringBuilderAdd·p0.90    sample              0.100          us/op
stringBuilderAdd:stringBuilderAdd·p0.95    sample              0.100          us/op
stringBuilderAdd:stringBuilderAdd·p0.99    sample              0.100          us/op
stringBuilderAdd:stringBuilderAdd·p0.999   sample              0.300          us/op
stringBuilderAdd:stringBuilderAdd·p0.9999  sample             10.896          us/op
stringBuilderAdd:stringBuilderAdd·p1.00    sample           3563.520          us/op

SingleShotTime 以上模式都是默认一次 iteration 是 1秒,唯有 SingleShotTime 是只运行一次。往往同时把 warmup 次数设为0,用于测试冷启动时的性能。

代码语言:javascript
复制
// 预热修改为0,执行的次数修改为1
Options opt = new OptionsBuilder()
                .include(JmhApplication.class.getSimpleName())
                .forks(1)
                .warmupIterations(0)
                .measurementIterations(1)
                .build();
// 结果如下
Benchmark                        Mode  Cnt      Score   Error  Units
JmhApplication.stringAdd           ss       32612.100          us/op
JmhApplication.stringBuilderAdd    ss          18.500          us/op

All 运行全部以上的基准测试模式

其中最常用的是Throughput和AverageTime模式,其他的在正常场景基本上可以略过。

后续

工具使用起来比较方便,功能也是挺多,此处不逐个介绍。一个很大的缺点是文档比较少,官方有完整的示例代码,可以直接阅读和执行来学习如何使用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 泛泛聊后端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 后续
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档