[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