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