JMH - Java代码基准测试工具,代码性能问题验证测试

一、前言

在日常开发工作当中,开发人员可能有这些困惑:自己写的这个方法性能到底怎么样?在原接口实现方法中添加了新的业务逻辑,对整个接口的性能影响有多少?有多种实现方式(或开源类库),到底哪一种性能更好?
当遇到类似困惑或者说问题的时候,怎么办呢?当然是测试验证,实践出真知!本文讲述的就是一个方法级别的性能测试工具—— JMH。

二、JMH概述

1、什么是JMH

JMH,即 Java Microbenchmark Harness,是专门用于代码微基准测试的工具套件。何谓Micro Benchmark呢?简单的来说就是基于方法层面的基准测试,精度可以达到微秒级。其由 Oracle/openjdk 内部开发JIT编译器的大佬们所开发,作为java的方法级性能测试工具可以说是根正苗红了。(官方地址:http://hg.openjdk.java.net/code-tools/jmh/

2、JMH适用的典型场景

  • a、优化热点方法,准确的知道某个方法的执行耗时,以及不同入参与最终实际耗时的关系,从而针对性的进行优化;
  • b、寻找最佳方案,验证接口方法不同实现方式的实际吞吐量,从而确定最佳实现方式 。如:选择json转换工具时选fastjson还是gson、字符串连接使用StringBuilder方式还是直接相加;
  • c、分析性能损耗,在原接口方法业务逻辑中添加新的业务代码时,对整个业务方法的性能影响。如:在原业务逻辑中,添加一个插入操作日志的操作,可以分析新加操作对整个业务方法的性能影响。
  • d、分析百分比内的耗时,即测试方法多次调用时百分比区间内的耗时,如:测试调用某个方法,50%以内的调用耗时是8.2ms/op,90%以内是9.3ms/op,99.99%以内是10.2ms/op,等等。(模式为Mode.SampleTime)

3、JMH基本概念

  • a、Mode :表示JMH测试中的模式,默认有5种,分别是Throughput(吞吐量)、AverageTime(平均耗时)、SampleTime(随机采样)、SingleShotTime(单次执行)、All(以上4种都来一次);
  • b、Fork:表示JMH将用来测试的进程数;
  • c、Warmup : 表示预热,在HotSpot中,JVM的JIT编译器会对热点代码进行编译优化, 因此为了最接近真实的情况,需要先预热测试代码,使JIT编译器完成可能需要的优化,从而令JMH最终测试结果更加准确;
  • d、Iteration :表示JMH中的最小测试迭代单位,即测试次数,一般默认值是每次1s;
  • e、Benchmark:用于标注JMH将进行测试的方法。(类似Junit中的@Test注解)

三、示例

1、添加依赖

    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-core</artifactId>
        <version>1.36</version>
    </dependency>
    <dependency>
        <groupId>org.openjdk.jmh</groupId>
        <artifactId>jmh-generator-annprocess</artifactId>
        <version>1.36</version>
        <scope>provided</scope>
    </dependency>

2、示例代码

package com.example.springbootdemo1;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;
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;

/**
 * try - catch 性能测试
 */
@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 1 轮,每次 1s
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Benchmark)
@Threads(100)
public class TryCatchPerformanceTest {
    
    
    private static final int forSize = 1000; // 循环次数

    public static void main(String[] args) throws RunnerException {
    
    
        // 启动基准测试
        Options opt = new OptionsBuilder().include(TryCatchPerformanceTest.class.getSimpleName()) // 要导入的测试类
                .build();
        new Runner(opt).run(); // 执行测试
    }

    @Benchmark
    public int innerForeach() {
    
    
        int count = 0;
        for (int i = 0; i < forSize; i++) {
    
    
            try {
    
    
                if (i == forSize) {
    
    
                    throw new Exception("new Exception");
                }
                count++;
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        return count;
    }

    @Benchmark
    public int outerForeach() {
    
    
        int count = 0;
        try {
    
    
            for (int i = 0; i < forSize; i++) {
    
    
                if (i == forSize) {
    
    
                    throw new Exception("new Exception");
                }
                count++;
            }
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        return count;
    }
}

代码拷贝就可以直接运行,其他更多测试请自行修改试验。

参考:https://www.uoften.com/article/215076.html


(END)

猜你喜欢

转载自blog.csdn.net/catoop/article/details/131598710
jmh