Optimize InstantDeserializer method replaceZeroOffsetAsZIfNecessary()#266
Optimize InstantDeserializer method replaceZeroOffsetAsZIfNecessary()#266cowtowncoder merged 3 commits intoFasterXML:2.15from
InstantDeserializer method replaceZeroOffsetAsZIfNecessary()#266Conversation
|
Excellent, thank you @schlosna! I especially appreciate jmh benchmark here. Just one small thing before I can merge this for inclusion in 2.15.0: unless I have asked for a CLA, I'd need it from: https://github.com/FasterXML/jackson/blob/master/contributor-agreement.pdf and the usual way is to print it, fill & sign, scan/photo, email to Thank you again & looking forward to merging this PR! |
|
Ok, one oddity: new test cases fail on JDK 8 and 11 (see CI run; I had to enable it since it's your first contribution). Possibly related to handling (or not) of colon in time offset parts? |
Avoid expensive regex matcher allocations when replacing zero offset with 'Z'. OpenJDK 17 on x64 Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Benchmark Mode Cnt Score Error Units InstantDeserializerBenchmark.index avgt 5 19.543 ± 1.384 ns/op InstantDeserializerBenchmark.regex avgt 5 155.081 ± 3.234 ns/op OpenJDK 17 on aarch64 Apple M1 Pro aarch64 Benchmark Mode Cnt Score Error Units InstantDeserializerBenchmark.index avgt 5 16.855 ± 0.537 ns/op InstantDeserializerBenchmark.regex avgt 5 58.155 ± 1.444 ns/op
|
Thanks for the quick review @cowtowncoder ! I'll get the CLA reviewed & signed hopefully tomorrow. I removed the commit with additional test cases as I forgot those will fail on JDK 8 & 11 (the test cases also fail on JDK 8 & 11 applied to |
These tests are skipped before JDK 12 as DateTimeFormatter.ISO_INSTANT didn't handle offsets until JDK 12+, see https://bugs.openjdk.org/browse/JDK-8166138
|
After some needed sleep, I realized why those tests pass on JDK 17+ but did not pass on JDK 8 & 11 and it is the reason we're replacing the zero offset with I've updated the test cases in |
| String maybeOffset = text.substring(maybeOffsetIndex); | ||
| if ("00".equals(maybeOffset) || "0000".equals(maybeOffset) || "00:00".equals(maybeOffset)) { |
There was a problem hiding this comment.
Can we optimize this using String.regionMatches?
perhaps using something along the lines of
switch (remaining) {
case 2:
return text.regionmatches..."00" ? text.substring(0, plusIndex) + 'Z' : text;
case 4:
..etc
case 5:
..etc
}
return text;There was a problem hiding this comment.
Seems reasonable to avoid the substring allocation, updated to use regionMatches & switch and reran JMH and tested locally with JDKs 8, 11, 17.
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
JDK 17.0.5, OpenJDK 64-Bit Server VM, 17.0.5+8-LTS
Benchmark Mode Cnt Score Error Units
InstantDeserializerBenchmark.index avgt 5 20.003 ± 0.486 ns/op
InstantDeserializerBenchmark.regex avgt 5 156.174 ± 7.654 ns/op
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
JDK 11.0.16.1, OpenJDK 64-Bit Server VM, 11.0.16.1+9-LTS
Benchmark Mode Cnt Score Error Units
InstantDeserializerBenchmark.index avgt 5 20.818 ± 1.267 ns/op
InstantDeserializerBenchmark.regex avgt 5 192.248 ± 7.341 ns/op
Apple M1 Pro aarch64
JDK 17.0.6, OpenJDK 64-Bit Server VM, 17.0.6+10-LTS
Benchmark Mode Cnt Score Error Units
InstantDeserializerBenchmark.index avgt 5 17.498 ± 0.677 ns/op
InstantDeserializerBenchmark.regex avgt 5 58.096 ± 1.330 ns/op
Apple M1 Pro aarch64
Benchmark Mode Cnt Score Error Units
InstantDeserializerBenchmark.index avgt 5 16.176 ± 7.842 ns/op
InstantDeserializerBenchmark.regex avgt 5 63.201 ± 5.668 ns/op
|
Ok sounds good; will merge as soon as we get CLA done. Good work everyone! |
|
I'll submit CLA ASAP, waiting on review right now |
|
CLA submitted via email |
InstantDeserializer method replaceZeroOffsetAsZIfNecessary()
|
Thank you @schlosna -- merged to be included in 2.15.0! |
While reviewing some JFRs from some services that heavily use Jackson for deserializing timestamps, I noticed that
com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer.replaceZeroOffsetAsZIfNecessary(String)was allocating a lot ofint[]from its use ofjava.util.regex.Pattern.matcher(CharSequence)to determine whether an input String is a zero offset and if so replace it withZ.I would like to propose an alternative implementation to avoid expensive regex matcher allocations when replacing zero offset with 'Z'.
Using a JMH benchmark with a variety of generated timestamps I see the following results on x64 (~7x speedup) and aarch64 (~2.5x speedup):
OpenJDK 17.0.6 on x64 Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
OpenJDK 17.0.6 on aarch64 Apple M1 Pro