JMH快速入门

在这里插入图片描述

JMH简介

JMH是专门用于代码微基准测试的工具集。JMH是由实现Java虚拟机的团队开发的,由于现在JVM已经变得越来越智能,在Java文件的编译阶段、类的加载阶段,以及运行阶段可能进行了不同程度的优化,因此开发者编写的代码在运行中未必会像自己所预期的那样具有相同的性能体现。

JMH快速入门

最常见的就是ArrayList和LinkedList的性能比较,我们分别进行1000组测试,每组测试都会将List执行1000000次的add调用。

用main方法测试

public class ArrayListVsLinkedList {
    private final static String DATA_STRING = "DUMMY DATA";
    private final static int MAX_CAPACITY = 1_000_000;
    private final static int MAX_ITERATIONS = 1000;

    private static void test(List<String> list) {
        for (int i = 0; i < MAX_CAPACITY; i++) {
            list.add(DATA_STRING);
        }
    }

    private static void arrayListPerfTest(int iterations) {
        final List<String> list = new ArrayList<>();
        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        test(list);
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }

    private static void linkListPerfTest(int iterations) {
        final List<String> list = new LinkedList<>();
        final StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        test(list);
        stopWatch.stop();
        System.out.println(stopWatch.prettyPrint());
    }

    public static void main(String[] args) {
        arrayListPerfTest(MAX_ITERATIONS);
        System.out.println(Strings.repeat('#',100));
        linkListPerfTest(MAX_ITERATIONS);
    }
}

在这里插入图片描述

乍一看ArrayList的add方法性能要好于LinkedList的add方法,事实上,ArrayLIst的随机写性能确实好于LinkedList(尤其是在ArrayList不进行内部数组扩容复制的条件下),LinkedList由于链表的设计,其delete操作的性能会好于ArrayList,我们这种测试会有几个问题:

  • 使用StopWatch进行时间计算,其实是在StopWatch内部记录了方法执行开始纳秒数,这种操作本质会导致消耗CPU时间的浪费。
  • 在代码的运行过程中,JVM可能会优化,比如循环展开、运行时编译等,这样会导致某组未经过优化的性能数据参与统计。

使用JMH做微基准测试

		<dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.19</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.19</version>
            <scope>provided</scope>
        </dependency>
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class JMHExample1 {
    private final static String DATA_STATE = "DUMMY DATA";
    private List<String> arrayList;
    private List<String> linkedList;

    @Setup(Level.Iteration)
    public void setup() {
        arrayList = new ArrayList<>();
        linkedList = new LinkedList<>();
    }

    @Benchmark
    public List<String> arrayListAdd() {
        arrayList.add(DATA_STATE);
        return arrayList;
    }

    @Benchmark
    public List<String> linkedListAdd() {
        linkedList.add(DATA_STATE);
        return linkedList;
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder().include(JMHExample1.class.getSimpleName()).forks(1).measurementIterations(10).warmupIterations(10).build();
        new Runner(opts).run();
    }
}

运行main方法,可以得到以下输出:

# JMH version: 1.19
# VM version: JDK 1.8.0_191, VM 25.191-b12
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=53530:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: fast.cloud.nacos.codejuc.jmh.JMHExampleA.arrayListAdd

# Run progress: 0.00% complete, ETA 00:00:40
# Fork: 1 of 1
# Warmup Iteration   1: ≈ 10⁻⁴ ms/op
# Warmup Iteration   2: ≈ 10⁻⁴ ms/op
# Warmup Iteration   3: ≈ 10⁻⁴ ms/op
# Warmup Iteration   4: ≈ 10⁻⁴ ms/op
# Warmup Iteration   5: ≈ 10⁻⁵ ms/op
# Warmup Iteration   6: ≈ 10⁻⁵ ms/op
# Warmup Iteration   7: ≈ 10⁻⁵ ms/op
# Warmup Iteration   8: ≈ 10⁻⁵ ms/op
# Warmup Iteration   9: ≈ 10⁻⁵ ms/op
# Warmup Iteration  10: ≈ 10⁻⁵ ms/op
Iteration   1: ≈ 10⁻⁵ ms/op
Iteration   2: ≈ 10⁻⁵ ms/op
Iteration   3: ≈ 10⁻⁵ ms/op
Iteration   4: ≈ 10⁻⁵ ms/op
Iteration   5: ≈ 10⁻⁵ ms/op
Iteration   6: ≈ 10⁻⁵ ms/op
Iteration   7: ≈ 10⁻⁵ ms/op
Iteration   8: ≈ 10⁻⁵ ms/op
Iteration   9: ≈ 10⁻⁵ ms/op
Iteration  10: ≈ 10⁻⁵ ms/op


Result "fast.cloud.nacos.codejuc.jmh.JMHExampleA.arrayListAdd":
  ≈ 10⁻⁵ ms/op


# JMH version: 1.19
# VM version: JDK 1.8.0_191, VM 25.191-b12
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=53530:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 10 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: fast.cloud.nacos.codejuc.jmh.JMHExampleA.linkedListAdd

# Run progress: 50.00% complete, ETA 00:00:27
# Fork: 1 of 1
# Warmup Iteration   1: ≈ 10⁻³ ms/op
# Warmup Iteration   2: ≈ 10⁻⁴ ms/op
# Warmup Iteration   3: ≈ 10⁻⁴ ms/op
# Warmup Iteration   4: ≈ 10⁻⁴ ms/op
# Warmup Iteration   5: ≈ 10⁻⁴ ms/op
# Warmup Iteration   6: ≈ 10⁻⁴ ms/op
# Warmup Iteration   7: ≈ 10⁻⁴ ms/op
# Warmup Iteration   8: ≈ 10⁻⁴ ms/op
# Warmup Iteration   9: ≈ 10⁻⁴ ms/op
# Warmup Iteration  10: ≈ 10⁻³ ms/op
Iteration   1: ≈ 10⁻⁴ ms/op
Iteration   2: ≈ 10⁻³ ms/op
Iteration   3: ≈ 10⁻⁴ ms/op
Iteration   4: ≈ 10⁻⁴ ms/op
Iteration   5: ≈ 10⁻⁴ ms/op
Iteration   6: ≈ 10⁻⁴ ms/op
Iteration   7: ≈ 10⁻⁴ ms/op
Iteration   8: ≈ 10⁻⁴ ms/op
Iteration   9: ≈ 10⁻⁵ ms/op
Iteration  10: ≈ 10⁻³ ms/op


Result "fast.cloud.nacos.codejuc.jmh.JMHExampleA.linkedListAdd":
  ≈ 10⁻⁴ ms/op


# Run complete. Total time: 00:01:17

Benchmark                  Mode  Cnt   Score    Error  Units
JMHExampleA.arrayListAdd   avgt   10  ≈ 10⁻⁵           ms/op
JMHExampleA.linkedListAdd  avgt   10  ≈ 10⁻⁴           ms/op

得出的结论是ArrayList的add方法性能要好于LinkedList的性能使用JMH显然更加严谨。

@Benchmark标记基准测试方法

如果一个类没有一个方法被@Benchmark标记的话,那么对其进行基准测试会报错。

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
public class JMHExample2 {
    public void normalMethod() {

    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder().include(JMHExample2.class.getSimpleName()).forks(1).measurementIterations(10).warmupIterations(10).build();
        new Runner(opts).run();
    }
}

结果如下:

在这里插入图片描述

@Warmup及@Measurement

这两个注解既可作用在类上,也可以作用在方法上,当类和方法上都有的时候,这个时候方法上的会覆盖掉类上面的,当Option中代码设置这两个字段话,优先级是最高的,注解就失效了。

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Measurement(iterations = 5)
@Warmup(iterations = 2)
public class JMHExample3 {
    @Benchmark
    public void test() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @Measurement(iterations = 10)
    @Warmup(iterations = 3)
    @Benchmark
    public void test2() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample3.class.getSimpleName())
                .forks(1)
//                .measurementIterations(10)
//                .warmupIterations(10)
                .build();
        new Runner(opts).run();
    }
}

这个时候可以看到的结果是方法上覆盖掉了类上面的,下面的方法运行了10次。


Benchmark          Mode  Cnt   Score   Error  Units
JMHExample3.test   avgt    5  10.931 ± 0.234  ms/op
JMHExample3.test2  avgt   10  11.035 ± 0.147  ms/op
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Measurement(iterations = 5)
@Warmup(iterations = 2)
public class JMHExample3 {
    @Benchmark
    public void test() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @Measurement(iterations = 10)
    @Warmup(iterations = 3)
    @Benchmark
    public void test2() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample3.class.getSimpleName())
                .forks(1)
                .measurementIterations(10)
                .warmupIterations(10)
                .build();
        new Runner(opts).run();
    }
}

这个时候可以看到options优先级最高,两个方法都运行了10次。

Benchmark          Mode  Cnt   Score   Error  Units
JMHExample3.test   avgt   10  10.916 ± 0.110  ms/op
JMHExample3.test2  avgt   10  10.820 ± 0.129  ms/op

四大BenchmarkMode

    Throughput("thrpt", "Throughput, ops/time"),
    AverageTime("avgt", "Average time, time/op"),
    SampleTime("sample", "Sampling time"),
    SingleShotTime("ss", "Single shot invocation time"),
    All("all", "All benchmark modes");

分别有四种:

类型解释
Throughput吞吐量
AverageTime平均响应时间
SampleTime时间采样
SingleShotTime冷测试
all前面四种
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@State(Scope.Thread)
@Measurement(iterations = 1)
@Warmup(iterations = 1)
public class JMHExample4 {
    @BenchmarkMode(Mode.AverageTime)
    @Benchmark
    public void testAverageTime() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @BenchmarkMode(Mode.Throughput)
    @Benchmark
    public void testThroughput() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @BenchmarkMode(Mode.SampleTime)
    @Benchmark
    public void testSampleTime() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @BenchmarkMode(Mode.SingleShotTime)
    @Benchmark
    public void testSingleShotTime() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @BenchmarkMode({Mode.AverageTime,Mode.Throughput})
    @Benchmark
    public void testAverageTimeAndThroughput() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    @BenchmarkMode(Mode.All)
    @Benchmark
    public void testAll() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(10);
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample4.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }
}
Benchmark                                            Mode  Cnt   Score   Error   Units
JMHExample4.testAll                                 thrpt        0.091          ops/ms
JMHExample4.testAverageTimeAndThroughput            thrpt        0.091          ops/ms
JMHExample4.testThroughput                          thrpt        0.091          ops/ms
JMHExample4.testAll                                  avgt       10.930           ms/op
JMHExample4.testAverageTime                          avgt       10.875           ms/op
JMHExample4.testAverageTimeAndThroughput             avgt       10.993           ms/op
JMHExample4.testAll                                sample   90  11.205 ± 0.317   ms/op
JMHExample4.testAll:testAll·p0.00                  sample       10.027           ms/op
JMHExample4.testAll:testAll·p0.50                  sample       11.092           ms/op
JMHExample4.testAll:testAll·p0.90                  sample       12.480           ms/op
JMHExample4.testAll:testAll·p0.95                  sample       12.541           ms/op
JMHExample4.testAll:testAll·p0.99                  sample       13.550           ms/op
JMHExample4.testAll:testAll·p0.999                 sample       13.550           ms/op
JMHExample4.testAll:testAll·p0.9999                sample       13.550           ms/op
JMHExample4.testAll:testAll·p1.00                  sample       13.550           ms/op
JMHExample4.testSampleTime                         sample   92  10.954 ± 0.229   ms/op
JMHExample4.testSampleTime:testSampleTime·p0.00    sample       10.027           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.50    sample       10.830           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.90    sample       11.878           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.95    sample       12.343           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.99    sample       12.452           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.999   sample       12.452           ms/op
JMHExample4.testSampleTime:testSampleTime·p0.9999  sample       12.452           ms/op
JMHExample4.testSampleTime:testSampleTime·p1.00    sample       12.452           ms/op
JMHExample4.testAll                                    ss       10.477           ms/op
JMHExample4.testSingleShotTime                         ss       11.428           ms/op

三大State的使用

public enum Scope {

    Benchmark
    Group,
    Thread,

}
类型描述
Benchmark线程共享
Group线程组共享,比如多线程两个方法操作一个变量
Thread每个线程都会持有一个独立的对象
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 5)
@Warmup(iterations = 10)
@Threads(5)
@Fork(1)
public class JMHExample6 {
    @State(Scope.Thread)
    //每个线程都会创建,可以看到5次 create instance
    public static class Test {
        public Test() {
            System.out.println("create instance");
        }

        public void method() {

        }
    }

    @Benchmark
    public void test(Test test) {
        test.method();
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample6.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }
}

运行结果如下:

... 省略
# Warmup Iteration   
1: create instance
create instance
create instance
create instance
create instance
... 省略...

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 5)
@Warmup(iterations = 10)
@Threads(5)
@Fork(1)
public class JMHExample7 {
    @State(Scope.Benchmark)
    public static class Test {
        public Test() {
            System.out.println("create instance");
        }

        public void method() {

        }
    }

    @Benchmark
    public void test(Test test) {
        test.method();
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample7.class.getSimpleName())
                .forks(1)
//                .timeUnit(TimeUnit.NANOSECONDS)
//                .measurementIterations(10)
//                .warmupIterations(10)
                .build();
        new Runner(opts).run();
    }
}

只能看到一次 create instance

Benchmark         Mode  Cnt  Score    Error  Units
JMHExample6.test  avgt    5  0.001 ±  0.001  us/op
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
@Fork(1)
public class JMHExample8 {
    @State(Scope.Group)
    public static class Test {
        public Test() {
            System.out.println("create instance");
        }

        public void write() {
            System.out.println("write");
        }

        public void read() {
            System.out.println("read");
        }
    }

    @Benchmark
    @GroupThreads(3)
    @Group("test")
    public void write(Test test) {
        test.write();
    }

    @Benchmark
    @GroupThreads(3)
    @Group("test")
    public void read(Test test) {
        test.read();
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample8.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }
}
# JMH version: 1.19
# VM version: JDK 1.8.0_191, VM 25.191-b12
# VM invoker: /Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home/jre/bin/java
# VM options: -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=60443:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8
# Warmup: 5 iterations, 1 s each
# Measurement: 10 iterations, 1 s each
# Timeout: 10 min per iteration
总共有6个线程执行基准测试方法,这6个线程都在同一个group中,其中,testReade和testWrite分别被三个方法执行。
# Threads: 6 threads (1 group; 3x "read", 3x "write" in each group), will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: fast.cloud.nacos.codejuc.jmh.JMHExample8.test

# Run progress: 0.00% complete, ETA 00:00:15
# Fork: 1 of 1
# Warmup Iteration   1: create instance
write
write
write
write
read
read
read
write
write
write

@Param的妙用

比如我们比较ConcurrentHashMap和synchronizedMap的put性能,我们可能会这样比较:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
//设置为线程共享资源
@State(Scope.Benchmark)
public class JMHExample9 {
    private Map<Long, Long> concurrentMap;
    private Map<Long, Long> synchronizedMap;

    @Setup
    public void setup() {
        concurrentMap = new ConcurrentHashMap<>();
        synchronizedMap = Collections.synchronizedMap(new HashMap<>());
    }

    @Benchmark
    public void testConCurrentMap() {
        concurrentMap.put(System.nanoTime(), System.nanoTime());
    }

    @Benchmark
    public void testSynchronizedMap() {
        synchronizedMap.put(System.nanoTime(), System.nanoTime());
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample9.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }
}

可以看到ConCurrentMap比SynchronizedMap性能要好

Benchmark                        Mode  Cnt   Score     Error  Units
JMHExample9.testConCurrentMap    avgt   10  12.961 ±  15.519  us/op
JMHExample9.testSynchronizedMap  avgt   10  29.269 ± 100.921  us/op

这里如果再新增两个不同类型的Map和两个针对这两个Map实现基准测试方法,很显然,这种方式存在代码冗余。因此JMH提供一个注解@Param,它使得参数可配置,也就是说一个参数在每一次的基准测试时都会有不同的值与之对应。

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
@State(Scope.Benchmark)
public class JMHExample10 {
    @Param({"1", "2", "3", "4"})
    private int type;

    private Map<Long, Long> map;

    @Setup
    public void setup() {
        switch (type) {
            case 1:
                map = new ConcurrentHashMap<>();
                break;
            case 2:
                map = new ConcurrentSkipListMap<>();
                break;
            case 3:
                map = new Hashtable<>();
                break;
            case 4:
                map = Collections.synchronizedMap(new HashMap<>());
                break;
            default:
                throw new IllegalArgumentException("illegal map type.");
        }
    }

    @Benchmark
    public void test() {
        map.put(System.nanoTime(), System.nanoTime());
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample10.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }
}

以上基准测试都是基于put方法进行的,也就是说并没有同时进行crud,因此单凭一个接口是不能说明哪个性能好是不够严谨的。

# Run complete. Total time: 00:03:55

Benchmark          (type)  Mode  Cnt    Score     Error  Units
JMHExample10.test       1  avgt   10  150.179 ± 440.894  us/op
JMHExample10.test       2  avgt   10   11.797 ±  24.911  us/op
JMHExample10.test       3  avgt   10  120.114 ± 526.112  us/op
JMHExample10.test       4  avgt   10   12.543 ±  17.122  us/op

JMH的测试套件

@Setup:一个基准测试执行前被调用,通常用于初始化资源
@TearDown:基准测试之后调用,关闭资源或者断言

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
@State(Scope.Thread)
public class JMHExample11 {
    //定义了list但是没有初始化
    private List<String> list;

    @Setup
    public void setup() {
        list = new ArrayList<>();
    }

    @Benchmark
    public void measureRight() {
        list.add("test");
    }

    @Benchmark
    public void measureWrong() {

    }

    @TearDown
    public void tearDown() {
        assert list.size() > 0 : "muster greeter than zero";
    }

    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample11.class.getSimpleName())
                .forks(1)
                .jvmArgs("-ea")
                .build();
        new Runner(opts).run();
    }

}

@CompilerControl

JVM真的会对我们的代码进行相关的优化么,下面用代码验证下:

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
@State(Scope.Thread)
public class JMHExample12 {

    @Benchmark
//    @CompilerControl(CompilerControl.Mode.EXCLUDE)
    public void test1() {

    }

    @Benchmark
//    @CompilerControl(CompilerControl.Mode.EXCLUDE)
    public void test2() {
        log(PI);
    }
    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample12.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }

}

Math.log方法是一个消耗CPU的方法,肯定要高于空方法,但是性能数据不相上下。

# Run complete. Total time: 00:00:34

Benchmark           Mode  Cnt  Score    Error  Units
JMHExample12.test1  avgt   10  0.001 ±  0.001  us/op
JMHExample12.test2  avgt   10  0.001 ±  0.001  us/op

把注释去掉

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(iterations = 10)
@Warmup(iterations = 5)
@Threads(5)
@State(Scope.Thread)
public class JMHExample12 {

    @Benchmark
    @CompilerControl(CompilerControl.Mode.EXCLUDE)
    public void test1() {

    }

    @Benchmark
    @CompilerControl(CompilerControl.Mode.EXCLUDE)
    public void test2() {
        log(PI);
    }
    public static void main(String[] args) throws RunnerException {
        Options opts = new OptionsBuilder()
                .include(JMHExample12.class.getSimpleName())
                .forks(1)
                .build();
        new Runner(opts).run();
    }

}

可以看到差距了,差不多两倍了。

# Run complete. Total time: 00:00:32

Benchmark           Mode  Cnt  Score   Error  Units
JMHExample12.test1  avgt   10  0.178 ± 0.007  us/op
JMHExample12.test2  avgt   10  0.260 ± 0.036  us/op
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值