美文网首页
Java String拼接性能分析

Java String拼接性能分析

作者: YesirCool | 来源:发表于2019-04-07 17:02 被阅读0次

    StringAdd, StringBuffer, StringBuilder, StringBuilderHelper性能对比分析

    测试环境

    硬件

    CPU

    Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
    
    基准速度:   1.80 GHz
    插槽: 1
    内核: 4
    逻辑处理器:  8
    虚拟化:    已启用
    L1 缓存:  256 KB
    L2 缓存:  1.0 MB
    L3 缓存:  6.0 MB
    

    内存

    8.0 GB
    
    速度: 2133 MHz
    已使用的插槽: 2/2
    外形规格:   Row of chips
    为硬件保留的内存:   154 MB
    

    软件

    IDEA ULTIMATE 2018.3
    JMH Benchmark框架 1.21
    

    测试类

    /**
     * 比较字符串直接相加和StringBuilder的效率
     */
    @BenchmarkMode(Mode.AverageTime)
    @Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
    @Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
    @State(Scope.Thread)
    @Threads(1)
    @Fork(1)
    @OutputTimeUnit(TimeUnit.MICROSECONDS)
    public class StringBuilderBenchmark {
        private String repeatItem;
        private int repeatTimes;
        
        @Setup
        public void init() {
            repeatItem = "Benchmark";
            repeatTimes = 100;
        }
    
        @Benchmark
        public void stringAdd(Blackhole bh) {
            String a = "";
            for (int i = 0; i < repeatTimes; i++) {
                a += repeatItem;
            }
            bh.consume(a);
        }
    
        @Benchmark
        public void stringBufferAdd(Blackhole bh) {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < repeatTimes; i++) {
                sb.append(repeatItem);
            }
            bh.consume(sb.toString());
        }
    
        @Benchmark
        public void stringBuilderAdd(Blackhole bh) {
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < repeatTimes; i++) {
                sb.append(repeatItem);
            }
            bh.consume(sb.toString());
        }
    
        @Benchmark
        public void stringBuilderHelperAdd(Blackhole bh) {
            StringBuilder sb = StringBuilderHelper.getStringBuilder();
            for (int i = 0; i < repeatTimes; i++) {
                sb.append(repeatItem);
            }
            bh.consume(sb.toString());
        }
    }
    
    public final class StringBuilderHelper {
        private static final ThreadLocal<StringBuilder> TLSB = ThreadLocal.withInitial(StringBuilder::new);
    
        private StringBuilderHelper() {
        }
    
        public static StringBuilder getStringBuilder() {
            StringBuilder stringBuilder = TLSB.get();
            stringBuilder.setLength(0);
            return stringBuilder;
        }
    }
    

    生成短字符串

    "Benchmark"重复拼接20次

    这里取平均耗时,单位微秒,下同。

    @Setup
    public void init() {
        repeatItem = "Benchmark";
        repeatTimes = 20;
    }
     
    Benchmark                                      Mode  Cnt  Score   Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  0.406 ± 0.010  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5  0.262 ± 0.005  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5  0.259 ± 0.033  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.152 ± 0.020  us/op
    

    "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接20次

    @Setup
    public void init() {
        repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
        repeatTimes = 20;
    }
    
    Benchmark                                      Mode  Cnt  Score   Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  1.717 ± 0.010  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5  0.861 ± 0.007  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5  0.846 ± 0.016  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.249 ± 0.023  us/op
    
    

    生成中长字符串

    "Benchmark"重复拼接100次

    @Setup
    public void init() {
        repeatItem = "Benchmark";
        repeatTimes = 100;
    }
     
    Benchmark                                      Mode  Cnt  Score   Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  8.103 ± 0.128  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5  1.198 ± 0.010  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5  1.113 ± 0.025  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5  0.663 ± 0.009  us/op
    

    "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接100次

    @Setup
    public void init() {
        repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
        repeatTimes = 100;
    }
    
    Benchmark                                      Mode  Cnt   Score   Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  38.761 ± 1.753  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5   3.432 ± 0.036  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5   3.240 ± 0.043  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5   1.367 ± 0.021  us/op
    
    

    生成长字符串

    "Benchmark"重复拼接1000次

    @Setup
    public void init() {
        repeatItem = "Benchmark";
        repeatTimes = 1000;
    }
    
    Benchmark                                      Mode  Cnt    Score    Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  761.627 ± 16.109  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5   10.885 ±  0.144  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5   10.016 ±  0.179  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5    7.148 ±  0.107  us/op
    

    "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接1000次

    @Setup
    public void init() {
        repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
        repeatTimes = 1000;
    }
    
    Benchmark                                      Mode  Cnt     Score    Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  4035.181 ± 59.044  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5    35.703 ±  9.170  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5    32.452 ±  5.626  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5    17.628 ±  5.808  us/op
    

    生成超长字符串

    "Benchmark"重复拼接10000次

    @Setup
    public void init() {
        repeatItem = "Benchmark";
        repeatTimes = 10000;
    }
    
    Benchmark                                      Mode  Cnt      Score      Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  78718.895 ± 1009.605  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5    131.177 ±   37.317  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5    119.158 ±   21.876  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5     81.608 ±    5.685  us/op
    

    "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接10000次

    @Setup
    public void init() {
        repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
        repeatTimes = 10000;
    }
    
    Benchmark                                      Mode  Cnt       Score        Error  Units
    StringBuilderBenchmark.stringAdd               avgt    5  704055.930 ± 991698.568  us/op
    StringBuilderBenchmark.stringBufferAdd         avgt    5     426.180 ±      1.483  us/op
    StringBuilderBenchmark.stringBuilderAdd        avgt    5     420.615 ±      2.914  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd  avgt    5     177.804 ±     17.385  us/op
    

    生成超长字符串+多线程

    "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复拼接10000次,10个线程

    @Threads(10)
    @Setup
    public void init() {
        repeatItem = "BenchmarkBenchmarkBenchmarkBenchmarkBenchmark";
        repeatTimes = 10000;
    }
    
    Benchmark                                       Mode  Cnt        Score       Error  Units
    StringBuilderBenchmark.stringAdd                avgt    5  6877607.658 ± 68654.544  us/op
    StringBuilderBenchmark.stringBufferAdd          avgt    5     3792.719 ±    33.263  us/op
    StringBuilderBenchmark.stringBuilderAdd         avgt    5     3780.851 ±    79.109  us/op
    StringBuilderBenchmark.stringBuilderHelperAdd   avgt    5     1387.908 ±    67.224  us/op
    

    结果分析

    • 短字符串,如"Benchmark"重复 20次,String+的性能是其他三种方法的1/2,差距不是太大。
    • 中长字符串开始,如"Benchmark"重复100次,String+的性能被拉开。
    • StringBuffer和StringBuilder在单线程场景下,性能非常接近,后者略优。
    • StringBuilderHelper(ThreadLocal版的StringBuilder)是性能最好的版本,尤其是大颗粒大字符串情况下("BenchmarkBenchmarkBenchmarkBenchmarkBenchmark"重复10000次对比"Benchmark"重复10000次),相较于StringBuilder和StringBuffer的性能,提升一倍多。
    • StringBuilderHelper在线程池下的效率会更高(因为线程不销毁,StringBuilder实例不会被销毁,能够最大程度复用)。

    相关文章

      网友评论

          本文标题:Java String拼接性能分析

          本文链接:https://www.haomeiwen.com/subject/tuokiqtx.html