View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.lang3;
19  
20  import java.util.concurrent.TimeUnit;
21  
22  import org.openjdk.jmh.annotations.Benchmark;
23  import org.openjdk.jmh.annotations.BenchmarkMode;
24  import org.openjdk.jmh.annotations.Fork;
25  import org.openjdk.jmh.annotations.Level;
26  import org.openjdk.jmh.annotations.Measurement;
27  import org.openjdk.jmh.annotations.Mode;
28  import org.openjdk.jmh.annotations.OutputTimeUnit;
29  import org.openjdk.jmh.annotations.Param;
30  import org.openjdk.jmh.annotations.Scope;
31  import org.openjdk.jmh.annotations.Setup;
32  import org.openjdk.jmh.annotations.State;
33  import org.openjdk.jmh.annotations.Warmup;
34  
35  /**
36   * Benchmark comparing the old and new implementations of CharSequenceUtils methods.
37   *
38   * <p>
39   * Run with:
40   * </p>
41   *
42   * <pre>
43   * mvn -P benchmark clean test -Dbenchmark=org.apache.commons.lang3.CharSequenceUtilsBenchmark
44   * </pre>
45   * <p>
46   * Results:
47   * </p>
48   *
49   * <pre>
50  Benchmark                                               (charSequenceType)  (length)  Mode  Cnt     Score    Error  Units
51  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent              String        10  avgt    5     1.626 ±  0.011  ns/op
52  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent              String        50  avgt    5     2.741 ±  0.029  ns/op
53  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent              String       100  avgt    5     4.235 ±  0.038  ns/op
54  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent              String       500  avgt    5    17.713 ±  0.273  ns/op
55  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent              String      1000  avgt    5    34.692 ±  1.752  ns/op
56  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent       StringBuilder        10  avgt    5     1.963 ±  0.047  ns/op
57  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent       StringBuilder        50  avgt    5     4.085 ±  0.042  ns/op
58  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent       StringBuilder       100  avgt    5     5.978 ±  0.177  ns/op
59  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent       StringBuilder       500  avgt    5    25.616 ±  1.621  ns/op
60  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent       StringBuilder      1000  avgt    5    53.749 ±  0.420  ns/op
61  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent        StringBuffer        10  avgt    5     7.239 ±  0.149  ns/op
62  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent        StringBuffer        50  avgt    5     9.061 ±  0.187  ns/op
63  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent        StringBuffer       100  avgt    5    10.281 ±  0.055  ns/op
64  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent        StringBuffer       500  avgt    5    29.647 ±  0.420  ns/op
65  CharSequenceUtilsBenchmark.benchmarkToCharArrayCurrent        StringBuffer      1000  avgt    5    56.203 ±  0.505  ns/op
66  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew                  String        10  avgt    5     1.657 ±  0.030  ns/op
67  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew                  String        50  avgt    5     2.771 ±  0.094  ns/op
68  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew                  String       100  avgt    5     4.281 ±  0.036  ns/op
69  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew                  String       500  avgt    5    17.744 ±  0.091  ns/op
70  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew                  String      1000  avgt    5    34.224 ±  0.251  ns/op
71  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew           StringBuilder        10  avgt    5     1.962 ±  0.128  ns/op
72  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew           StringBuilder        50  avgt    5     4.101 ±  0.035  ns/op
73  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew           StringBuilder       100  avgt    5     5.984 ±  0.062  ns/op
74  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew           StringBuilder       500  avgt    5    25.448 ±  0.152  ns/op
75  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew           StringBuilder      1000  avgt    5    54.531 ±  0.559  ns/op
76  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew            StringBuffer        10  avgt    5     7.260 ±  0.175  ns/op
77  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew            StringBuffer        50  avgt    5     8.537 ±  0.101  ns/op
78  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew            StringBuffer       100  avgt    5    10.502 ±  0.143  ns/op
79  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew            StringBuffer       500  avgt    5    29.584 ±  0.339  ns/op
80  CharSequenceUtilsBenchmark.benchmarkToCharArrayNew            StringBuffer      1000  avgt    5    56.751 ±  0.983  ns/op
81  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld                  String        10  avgt    5     1.656 ±  0.231  ns/op
82  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld                  String        50  avgt    5     2.770 ±  0.222  ns/op
83  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld                  String       100  avgt    5     4.298 ±  0.198  ns/op
84  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld                  String       500  avgt    5    18.023 ±  0.203  ns/op
85  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld                  String      1000  avgt    5    35.053 ±  1.467  ns/op
86  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld           StringBuilder        10  avgt    5     3.164 ±  0.062  ns/op
87  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld           StringBuilder        50  avgt    5     8.907 ±  0.185  ns/op
88  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld           StringBuilder       100  avgt    5    15.801 ±  0.104  ns/op
89  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld           StringBuilder       500  avgt    5    77.203 ±  0.460  ns/op
90  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld           StringBuilder      1000  avgt    5   164.064 ±  2.506  ns/op
91  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld            StringBuffer        10  avgt    5    28.981 ±  0.307  ns/op
92  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld            StringBuffer        50  avgt    5   126.285 ±  1.688  ns/op
93  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld            StringBuffer       100  avgt    5   250.584 ±  5.639  ns/op
94  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld            StringBuffer       500  avgt    5  1231.478 ± 51.296  ns/op
95  CharSequenceUtilsBenchmark.benchmarkToCharArrayOld            StringBuffer      1000  avgt    5  2453.553 ± 54.004  ns/op
96   * </pre>
97   *
98   */
99  @BenchmarkMode(Mode.AverageTime)
100 @OutputTimeUnit(TimeUnit.NANOSECONDS)
101 @State(Scope.Thread)
102 @Fork(1)
103 @Warmup(iterations = 3, time = 1)
104 @Measurement(iterations = 5, time = 1)
105 public class CharSequenceUtilsBenchmark {
106 
107     /**
108      * New optimized implementation of toCharArray.
109      */
110     public static char[] toCharArrayNew(final CharSequence source) {
111         final int len = StringUtils.length(source);
112         if (len == 0) {
113             return ArrayUtils.EMPTY_CHAR_ARRAY;
114         }
115         if (source instanceof String) {
116             return ((String) source).toCharArray();
117         }
118         // NEW: Uses bulk getChars() for StringBuilder/StringBuffer
119         if (source instanceof StringBuilder) {
120             final char[] array = new char[len];
121             ((StringBuilder) source).getChars(0, len, array, 0);
122             return array;
123         }
124         if (source instanceof StringBuffer) {
125             final char[] array = new char[len];
126             ((StringBuffer) source).getChars(0, len, array, 0);
127             return array;
128         }
129         final char[] array = new char[len];
130         for (int i = 0; i < len; i++) {
131             array[i] = source.charAt(i);
132         }
133         return array;
134     }
135 
136     /**
137      * Old implementation of toCharArray.
138      */
139     public static char[] toCharArrayOld(final CharSequence source) {
140         final int len = StringUtils.length(source);
141         if (len == 0) {
142             return ArrayUtils.EMPTY_CHAR_ARRAY;
143         }
144         if (source instanceof String) {
145             return ((String) source).toCharArray();
146         }
147         // OLD: Always uses charAt() loop, even for StringBuilder/StringBuffer
148         final char[] array = new char[len];
149         for (int i = 0; i < len; i++) {
150             array[i] = source.charAt(i);
151         }
152         return array;
153     }
154 
155     @Param({ "10", "50", "100", "500", "1000" })
156     public int length;
157     @Param({ "String", "StringBuilder", "StringBuffer" })
158     public String charSequenceType;
159     private CharSequence testSequence;
160 
161     @Benchmark
162     public char[] benchmarkToCharArrayCurrent() {
163         return CharSequenceUtils.toCharArray(testSequence);
164     }
165     @Benchmark
166     public char[] benchmarkToCharArrayNew() {
167         return toCharArrayNew(testSequence);
168     }
169 
170     @Benchmark
171     public char[] benchmarkToCharArrayOld() {
172         return toCharArrayOld(testSequence);
173     }
174 
175     @Setup(Level.Trial)
176     public void setup() {
177         final StringBuilder sb = new StringBuilder(length);
178         for (int i = 0; i < length; i++) {
179             sb.append((char) ('a' + i % 26));
180         }
181         final String content = sb.toString();
182         switch (charSequenceType) {
183         case "String":
184             testSequence = content;
185             break;
186         case "StringBuilder":
187             testSequence = new StringBuilder(content);
188             break;
189         case "StringBuffer":
190             testSequence = new StringBuffer(content);
191             break;
192         }
193     }
194 }