RFR 8170348: Appendable.appendN(char, int) method to append multiple copies of char
Peter Levart
peter.levart at gmail.com
Fri Dec 9 13:43:43 UTC 2016
Hi Ivan,
On 12/04/2016 01:07 PM, Ivan Gerasimov wrote:
> Hello!
>
> There are several places in JDK where the same character is appended
> to a StringBuilder object multiple times (usually padding).
> With each append there are a few routine checks performed.
> They could have been done only once, if we had a method for appending
> multiple copies at a time.
> A simple benchmark shows that such method may save us a few machine
> cycles (see the results below).
>
> In the benchmark, three approaches were compared:
> 0) Using the new appendN(char, int) method to append several chars at
> once,
> 1) Calling append(char) in a loop,
> 2) Appending a prepared-in-advance string
>
> On my machine, the new method demonstrates better or comparable
> performance for all sizes up to 20.
>
> In the webrev, there are two changesets included:
> - the new default Appendable.appendN(char, int) method, its overrides
> in StringBuilder/Buffer and a basic test,
> - several applications of the new method across JDK.
>
> Would you please help review?
> Comments, suggestions are welcome.
>
> BUGURL: https://bugs.openjdk.java.net/browse/JDK-8170348
> WEBREV: http://cr.openjdk.java.net/~igerasim/8170348/00/webrev/
> Benchmark:
> http://cr.openjdk.java.net/~igerasim/8170348/00/MyBenchmark.java
>
>
> Benchmark (size) Mode Cnt Score Error Units
> MyBenchmark.test_0_New 0 thrpt 70 331922128.215 ±
> 16399254.452 ops/s
> MyBenchmark.test_0_New 1 thrpt 70 209207932.893 ±
> 14955800.231 ops/s
> MyBenchmark.test_0_New 5 thrpt 70 72926671.621 ±
> 4841791.555 ops/s
> MyBenchmark.test_0_New 10 thrpt 70 67779575.053 ±
> 3234366.239 ops/s
> MyBenchmark.test_0_New 20 thrpt 70 59731629.661 ±
> 2769497.288 ops/s
> MyBenchmark.test_1_Old 0 thrpt 70 333467628.860 ±
> 15981678.430 ops/s
> MyBenchmark.test_1_Old 1 thrpt 70 156126381.967 ±
> 9619653.294 ops/s
> MyBenchmark.test_1_Old 5 thrpt 70 46550204.382 ±
> 2009987.637 ops/s
> MyBenchmark.test_1_Old 10 thrpt 70 23309297.849 ±
> 1268874.282 ops/s
> MyBenchmark.test_1_Old 20 thrpt 70 13143637.821 ±
> 662265.103 ops/s
> MyBenchmark.test_2_Solid 0 thrpt 70 138548108.540 ±
> 6408775.462 ops/s
> MyBenchmark.test_2_Solid 1 thrpt 70 63890936.132 ±
> 3918274.970 ops/s
> MyBenchmark.test_2_Solid 5 thrpt 70 65838879.075 ±
> 2701493.698 ops/s
> MyBenchmark.test_2_Solid 10 thrpt 70 65387238.993 ±
> 3131562.548 ops/s
> MyBenchmark.test_2_Solid 20 thrpt 70 57528150.828 ±
> 3171453.716 ops/s
>
>
> With kind regards,
> Ivan
>
An alternative to a new virtual method on Appendable (or maybe a
complement to it) could be a special internal CharSequence
implementation (CharRepetitions) with a static factory method on
CharSequence like the following:
http://cr.openjdk.java.net/~plevart/jdk9-dev/8170348_Appendable.appendN.alt/webrev.01/
Together with special-case optimization in
AbstractStringBuilder.append(CharSequence) it can perform equally well
when JITed. I took your benchmark and modified it a bit:
http://cr.openjdk.java.net/~plevart/jdk9-dev/8170348_Appendable.appendN.alt/AppendNTest.java
...I moved sb.setLength(0) into a special @Setup method so that it
doesn't cause the remaining tested code to be over-optimized. You can
try just this change in your benchmark and you'll notice a difference.
Comparing StringBuilder.appendN(c, n) with
StringBuilder.append(CharSequence.repetitions(c, n)) shows they perform
on par. Unsurprisingly since CharRepetitions objects is just as a
wrapper used to transport parameters (c, n) to the appendN() method and
EA makes sure its allocation on the heap is eliminated.
Benchmark (size) Mode Cnt Score Error Units
AppendNTest.test_0_New 0 avgt 60 15.676 ± 0.098 ns/op
AppendNTest.test_0_New 1 avgt 60 19.293 ± 0.024 ns/op
AppendNTest.test_0_New 5 avgt 60 22.042 ± 0.156 ns/op
AppendNTest.test_0_New 10 avgt 60 22.455 ± 0.453 ns/op
AppendNTest.test_0_New 20 avgt 60 23.539 ± 0.280 ns/op
AppendNTest.test_1_Old 0 avgt 60 15.818 ± 0.124 ns/op
AppendNTest.test_1_Old 1 avgt 60 19.628 ± 0.173 ns/op
AppendNTest.test_1_Old 5 avgt 60 27.870 ± 0.521 ns/op
AppendNTest.test_1_Old 10 avgt 60 38.956 ± 0.545 ns/op
AppendNTest.test_1_Old 20 avgt 60 61.895 ± 1.758 ns/op
AppendNTest.test_2_Solid 0 avgt 60 21.610 ± 0.117 ns/op
AppendNTest.test_2_Solid 1 avgt 60 25.055 ± 0.208 ns/op
AppendNTest.test_2_Solid 5 avgt 60 24.094 ± 0.133 ns/op
AppendNTest.test_2_Solid 10 avgt 60 24.178 ± 0.081 ns/op
AppendNTest.test_2_Solid 20 avgt 60 24.708 ± 0.101 ns/op
AppendNTest.test_3_Repeat 0 avgt 60 15.580 ± 0.031 ns/op
AppendNTest.test_3_Repeat 1 avgt 60 19.314 ± 0.094 ns/op
AppendNTest.test_3_Repeat 5 avgt 60 21.520 ± 0.063 ns/op
AppendNTest.test_3_Repeat 10 avgt 60 22.242 ± 0.027 ns/op
AppendNTest.test_3_Repeat 20 avgt 60 23.125 ± 0.074 ns/op
CharSequence.repetitions() is also a nice factory for String(s) with
repeated characters:
CharSequence.repetitions(c, n).toString()
Regards, Peter
More information about the core-libs-dev
mailing list