性能测试JMH

说在前面

StringBuilder和String谁快?快多少?为什么在大量字符拼接要用StringBuilder?
取模和并计算谁快?快多少?为什么大部分开源项目都采用并计算替换取模?

你在开发中是否也遇到如上的问题,以上当然只是技术海洋的冰山一角,你在工作中,学习中遇到类似的问题数不胜数
不知道多少人像我一样,看看技术爽文,心里虽然也知道StringBulder当然比String快啦,毕竟String会不停的创建中间对象,并计算当然比取模快拉,毕竟二进制运算是最快的
但是久而久之,我们都逐渐沦为 “的司机”,仅仅停留在表面,没有用真正的数据来量化比较过它们,又怎么会理解其底层设计的精华,更不会想着去做优化

但是有人会说了,要是每针对这样的场景都要自己去写个测试用例,成本过高而且考虑的方向比较狭隘,导致结果也不清不楚
因此在这里要给大家介绍一个工具JMH,帮助大家通过量化的数据来进行比较,从而告别含糊不清的描述,相信这是每个极客或者数据人的追求

生成视图的网站(在跑性能测试时指定生成json格式的结果,再上传到此网站可视化):
http://deepoove.com/jmh-visual-chart/ (效果相对较好,但是偶尔会故障)
https://jmh.morethan.io/

实战

注:JDK9后自带JMK

  1. 添加pom依赖
        <!-- JMH-->
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-core</artifactId>
            <version>1.25.2</version>
        </dependency>
        <dependency>
            <groupId>org.openjdk.jmh</groupId>
            <artifactId>jmh-generator-annprocess</artifactId>
            <version>1.25.2</version>
            <scope>provided</scope>
        </dependency>
  1. 增加性能测试类,在需要做性能测试的方法上添加@Benchnmark注解
    场景:验证字符串拼接的性能差异
@BenchmarkMode(Mode.Throughput)
@Warmup(iterations = 3)
@Measurement(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS)
@Threads(8)
@Fork(2)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class StringBuilderBenchmark {

        @Benchmark
        public void testStringAdd() {
            String a = "";
            for (int i = 0; i < 1000; i++) {
                a += i;
            }
            print(a);
        }

        @Benchmark
        public void testStringBuilderAdd() {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < 1000; i++) {
                stringBuilder.append(i);
            }
            print(stringBuilder.toString());
        }

        private void print(String a) {
        }

        // 生成json格式的结果,默认是生成log格式的
        public static void main(String []args) throws RunnerException {
            org.openjdk.jmh.runner.options.Options options = new OptionsBuilder()
                    .include(StringBuilderBenchmark.class.getSimpleName())
                    .shouldDoGC(true)
                    .resultFormat(ResultFormatType.JSON)
                    .result("benchmark-result.json")
                    .addProfiler(StackProfiler.class)
                    .jvmArgsAppend("-Djmh.stack.period=1")
                    .warmupIterations(5)
                    .measurementIterations(5)
                    .forks(1)
                    .build();
            new Runner(options).run();
        }
}

如下图可见,String的1000此拼接耗时大约为0.6 ms,约为StringBuilder的6倍,因此在做字符串拼接时,我们应优先考虑StringBuilder

由以下分析可知,在拼接10,50,100个字符时,使用String和StringBuilder每秒分别可操作的次数

参考文档

  1. https://github.com/Sayi/jmh-visual-chart
  2. https://www.xncoding.com/2018/01/07/java/jmh.html
  3. https://cloud.tencent.com/developer/article/1646596
  4. https://segmentfault.com/a/1190000020068026
posted @ 2020-10-10 17:34  墨小雨的猫  阅读(213)  评论(0)    收藏  举报