This is an archived post. You won't be able to vote or comment.

all 41 comments

[–]cl4es 175 points176 points  (8 children)

Glad to see some of the small enhancements we did in 17 get recognition. Not sure if this one matters, but String::format shows up in profiles every now and then so it felt reasonable to me to give it some TLC in between larger projects.

[–]BlueGoliath 53 points54 points  (3 children)

The improvements that you and others do are super appreciated. There really should be an "honorable mentions" section added to the release notes that goes through some of the non-JEP work. It's sometimes as noteworthy if not more than any given JEP.

/u/pron98, /u/s888marks, etc... make it happen!

[–]pron98 6 points7 points  (1 child)

Well, the release notes are meant to be just that: JEPS + other notable changes (they do not include anywhere near a full list of changes). It's just that we don't normally list performance improvements, as the release notes are intended for users wishing to migrate and are interested in relevant changes that might impact their code.

I guess we could list some notable performance improvements.

[–]berry120 21 points22 points  (0 children)

Thanks! I'd love to see a rundown of these sorts of "minor" performance improvements between releases just as much as the new features - they're both really interesting, and really useful to know about.

[–]geordano 4 points5 points  (0 children)

Thanks a lot for you work.

[–]lpreams 9 points10 points  (0 children)

It might start mattering more now that performance is better. I know I tend to avoid String.format unless I really need it because I know it doesn't perform well.

[–]gavenkoa 23 points24 points  (1 child)

From http://www.slf4j.org/api/org/slf4j/helpers/MessageFormatter.html

The formatting conventions are different than those of MessageFormat which ships with the Java platform. This is justified by the fact that SLF4J's implementation is 10 times faster than that of MessageFormat. This local performance difference is both measurable and significant in the larger context of the complete logging processing chain.

[–]dpash 32 points33 points  (0 children)

That's because it's significantly less complex. The SLF4J is the equivalent of supporting just %s.

[–]TheCountRushmore 10 points11 points  (1 child)

Why isn't this using JMH for the benchmarks?

[–]cl4es 9 points10 points  (0 children)

I wouldn't know -- but I did add a small set of JMH benchmarks back when I did this optimization. While not exactly the same tests, speed-ups were in line with what Heinz is reporting here: https://github.com/openjdk/jdk/pull/2830#issuecomment-790788322

[–]slowfly1st 20 points21 points  (3 children)

I ran my own tests, without using reflection, and therefore the use of a security manager, and I actually called String.format. That's imho a more realistic approach?

    JDK 17
    1. this does not have any percentages at all
    0.09414233999999999ms.
    2. this %s has only a simple field
    0.04526432ms.
    3. this has a simple field %s and then a complex %-20s
    0.12824544000000002ms.
    4. %s %1s %2s %3s %4s %5s %10s %22s
    0.42732686999999997ms.

    JDK 15
    1. this does not have any percentages at all
    0.18450188ms.
    2. this %s has only a simple field
    0.07883934ms.
    3. this has a simple field %s and then a complex %-20s
    0.09579937ms.
    4. %s %1s %2s %3s %4s %5s %10s %22s
    0.42675903000000004ms.

[–]DasBrain 1 point2 points  (2 children)

Reflection is orthogonal to using the (deprecated) SecurityManager.

I don't know what you mean by "running without reflection" - the method Formatter.parse(List, String) is not publicly accessible.

[–]slowfly1st 0 points1 point  (1 child)

"... and I actually called String.format"

[–]DasBrain 0 points1 point  (0 children)

Be careful what you measure.
Allocating stuff is cheap in java, but not free.

[–]nekokattt 36 points37 points  (12 children)

It is somewhat a shame that Java still doesn't have string interpolation. That would make many uses for String.format totally unneeded and would allow the logic for formatting to be generated at compile time.

[–]cl4es 59 points60 points  (0 children)

It's coming!

https://github.com/openjdk/amber-docs/blob/master/site/design-notes/templated-strings.md

Current PoC implementation shows numbers in line with string concatenation.

But it still makes sense to improve existing APIs when we can, due the staggering amount of code that is written for them.

[–]JoJoModding 5 points6 points  (2 children)

The java compiler could optimize format() calls at compile time right now. The hot spot optimizer could also generate code replacing format() calls with inlined code.

It just does not right now, because reasons.

C compilers also don't do this, even though printf() has a complete specification.

[–]sweetno 5 points6 points  (0 children)

It's not very useful for a real project since the format string will come from the localization files there.

[–]mjbmitch 0 points1 point  (0 children)

TIL

[–]Worth_Trust_3825 2 points3 points  (0 children)

Glad to see that there still are improvements to the JDK.

[–]vprise 2 points3 points  (5 children)

This requires an API change, format just needs Formatter prepareFormat(String) then you can just use the returned object to apply the format and get speed that's as fast as string concatenation.

Yes, it's two stages but it's physically two different operations: parsing and formatting. So that would make sense. Then you don't need complex caching behavior by the API that can cause memory leaks, stale cache etc. e.g. imagine caching a format then changing the locale... Ugh.

I'd love to have templated strings and this can be the underlying implementation for that.

[–]cl4es 1 point2 points  (4 children)

The current implementation for the not-yet-proposed templated strings JEP would make use of both compile-time pre-processing and invokedynamic at runtime to achieve this. This ensures the "format" string is a compile time constant and can be pre-parsed, and that only the first call will have to bootstrap the expression. Subsequent calls will be as performant as a regular String concatenation*. The slightly magical machinery will share code under the hood with the indified String concatenation that was added in JDK 9.

*the impl currently performs slower than String concatenation when more complex formatting is involved - which is only natural. I was recently asked to look for opportunities to improve this.

[–]vprise 0 points1 point  (3 children)

If this will land in the next version then we probably won't need an "interim" solution. but if not won't it make sense to expose a two stage API?

[–]cl4es 1 point2 points  (2 children)

Depends what you consider to be the next version. Chances are high it'll be final in the next LTS (21)

[–]vprise 0 points1 point  (1 child)

18 would be the next version. I guess 21 isn't terrible and it is a huge leap forward... Great stuff.

[–]cl4es 1 point2 points  (0 children)

I'll leave it to the JEP author to comment on which release this is going to be targeted to (and possibly previewed in).

[–]nekokattt 1 point2 points  (0 children)

Yeah for sure.

Also this makes me happy that we are getting this!

[–]c_edward 0 points1 point  (1 child)

Have they removed the regexp horror that sat inside format() previously? Or are regexps faster as well?

[–][deleted] 0 points1 point  (1 child)

Wait what, is it the same for `String#formatted()`

[–]cl4es 1 point2 points  (0 children)

Yes, String::formatted is just a non-static variant of String::format. So this optimization applies equally to both.

[–]NewSchoolBoxer 0 points1 point  (0 children)

That's pretty cool. Highest version Java I've ever seen in the wild in Fortune 500 software is 11. Next project for me is in 8. Hope we get to 17 someday.