[PATCH] Use StringJoiner where appropriate in java.base

Kasper Nielsen kasperni at gmail.com
Thu Jun 20 11:17:18 UTC 2019


On Thu, 20 Jun 2019 at 11:55, Peter Levart <peter.levart at gmail.com> wrote:
>
> Hi,
>
> On 6/20/19 10:50 AM, Kasper Nielsen wrote:
> > Hi,
> >
> > On Wed, 19 Jun 2019 at 14:12, Сергей Цыпанов <sergei.tsypanov at yandex.ru> wrote:
> >> Hello,
> >>
> >> in JDK code base we have many places (mainly in j.u.Arrays) where we convert array to String using verbose constructions with StringBuilder.
> >>
> >> As far as we have got StringJoiner for a long time we can use it making the code more simple.
> > It may make the code simpler, but it also comes with a hefty
> > performance penalty, taking twice as long in most cases compared to
> > the existing code.
> >
> > A quick benchmark toString'ing int arrays of size 1,10,100,1000
> >
> > Benchmark                    (size)  Mode  Cnt      Score     Error  Units
> > ToString2.toStringExisting        1  avgt    5     16.675 ±   0.327  ns/op
> > ToString2.toStringExisting       10  avgt    5     78.467 ±   1.373  ns/op
> > ToString2.toStringExisting      100  avgt    5    801.956 ±   7.517  ns/op
> > ToString2.toStringExisting     1000  avgt    5  14944.235 ± 155.393  ns/op
> >
> > ToString2.toStringSuggested       1  avgt    5     35.053 ±   0.533  ns/op
> > ToString2.toStringSuggested      10  avgt    5    222.043 ±  10.157  ns/op
> > ToString2.toStringSuggested     100  avgt    5   2150.948 ±  13.285  ns/op
> > ToString2.toStringSuggested    1000  avgt    5  23411.264 ± 201.721  ns/op
> >
> > /Kasper
>
> StringJoiner may be faster if you already have String objects at hand,
> since it is able to exactly pre-size the target array and need not
> copy/resize it later, but if you append primitive types, then creating
> intermediate String objects referenced from a data structure - the
> StringJointer (so they can not be scalarized by JIT) is contra-productive.
>
> An interesting test would be to run Kasper's JMH benchmark with "-prof
> gc" option. I think it will show more garbage created per call too.

Yes, less garbage with StringBuilder (existing)

ToString2.toStringExisting:·gc.alloc.rate.norm                 1  avgt
   5     80.000 ±     0.001    B/op
ToString2.toStringExisting:·gc.alloc.rate.norm                10  avgt
   5    160.000 ±     0.001    B/op
ToString2.toStringExisting:·gc.alloc.rate.norm               100  avgt
   5   1664.000 ±     0.001    B/op
ToString2.toStringExisting:·gc.alloc.rate.norm              1000  avgt
   5  23536.006 ±     0.001    B/op

ToString2.toStringSuggested:·gc.alloc.rate.norm                1  avgt
   5    168.000 ±     0.001    B/op
ToString2.toStringSuggested:·gc.alloc.rate.norm               10  avgt
   5    760.000 ±     0.001    B/op
ToString2.toStringSuggested:·gc.alloc.rate.norm              100  avgt
   5   7144.001 ±     0.001    B/op
ToString2.toStringSuggested:·gc.alloc.rate.norm             1000  avgt
   5  71024.010 ±     0.001    B/op

If you allowed a two-time pass of the primitive array you could
actually create a version that only allocated the actual String and
backing array.
But don't know if it is worth it. But then again I'm always surprised
by the amount of string processing being done.

/Kasper


More information about the core-libs-dev mailing list