扫码阅读
手机扫码阅读

Java微基准测试神器JMH初探

90 2024-04-13

当我们编写一段Java代码之后,如果想知道代码性能如何,就需要进行一些快速的性能测试。

当我们实现一个需求,面临2种及以上的方案,选择一种性能更好的方案时,也需要进行一些快速的性能测试。

在之前的实践中,我一开始的测试代码通常是这样的:

 public static void main(String[] args) { long start = System.currentTimeMillis(); for (int i = 0; i < 100000; i++) {
            doSomething();
        } long end = System.currentTimeMillis();
        System.out.println(end - start);
    } 

再到后来,变成这种:

 public static void main(String[] args) {
        def test = {
                doSomething()
        } new FunQpsConcurrent(test, "Demo").start();
    } 

但最近又有了新的工具JMH(Java Microbenchmark Harness)是用于代码微基准测试的工具套件,主要是基于方法层面的基准测试,精度可以达到纳秒级。该工具是由 Oracle 内部实现 JIT 的大牛们编写的,他们应该比任何人都了解 JIT 以及 JVM 对于基准测试的影响。

真可谓相见恨晚,上手也是非常迅速的,建议学习时间2小时,顺便看看官方GitHub仓库里面的错误示范,地址:https://github.com/lexburner/JMH-samples。

PS:

  1. 如果你用的Intellij,插件是真香。
  2. 如果要汇报,建议使用可视化工具,搜一下就有。

下面我分享一下我用JMH测试System.currentTimeMillis();System.nanoTime();两个方法的性能。外行大胆猜测,第二个方法应该性能比较差。原因是第二个方法获取到纳秒时间戳,应该会更消耗性能。

这是我的测试用例:

package com.funtest.groovytest; import com.funtester.frame.SourceCode; import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.results.format.ResultFormatType; 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.concurrent.TimeUnit; @BenchmarkMode(Mode.Throughput) @Warmup(iterations = 2, time = 5, timeUnit = TimeUnit.SECONDS) @Measurement(iterations = 5, time = 10, timeUnit = TimeUnit.SECONDS) @Threads(2) @Fork(1) @State(value = Scope.Thread) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class JmhT extends SourceCode { @Param(value = {"10", "20", "50"}) private int length; @Benchmark public void mill() {
        System.currentTimeMillis();
    } @Benchmark public void nano() {
        System.nanoTime();
    } public static void main(String[] args) throws RunnerException {
        Options options = new OptionsBuilder()
                .include(JmhT.class.getSimpleName())
                .result("result.json")
                .resultFormat(ResultFormatType.JSON)
                .forks(1)
                .threads(20)
                .warmupIterations(2)
                .warmupBatchSize(1)
                .measurementIterations(2)
                .measurementBatchSize(2)
                .build(); new Runner(options).run();
    }
} 

最后的main方法是为了生产result.json做可视化用的,如果单纯想知道性能差异,可以直接使用Intellij插件完成。用例中的length完全不用,只是想展示一下参数化的用法。

文字版测试结果如下:

Benchmark  (length)   Mode  Cnt  Score   Error   Units
JmhT.mill        10  thrpt    2  0.015          ops/ns
JmhT.mill        20  thrpt    2  0.015          ops/ns
JmhT.mill        50  thrpt    2  0.016          ops/ns
JmhT.nano        10  thrpt    2  0.091          ops/ns
JmhT.nano        20  thrpt    2  0.088          ops/ns
JmhT.nano        50  thrpt    2  0.084          ops/ns 

总体看就是System.nanoTime();性能要远远优于System.currentTimeMillis();,这是不是有点意外。

图片版本的报告

JMH性能测试报告
原文链接: http://mp.weixin.qq.com/s?__biz=MzU4MTE2NDEyMQ==&mid=2247498985&idx=1&sn=c91c69a2d48df79a0cf13e480aa63c33&chksm=fd4971dfca3ef8c99458b989e32c237daa31211051d4e2579c1f99c2ca06103b74608125fe55#rd