Friday has always been a good time for making performance improvements for me, for some reason. Today, its target was javax.xml.datatype.XMLGregorianCalendar class.
One of our internal benchmark identified this as a hotspot for JAXB, because when a schema uses xs:dateTime or some such datatype, we're binding it to XMLGregorianCalendar. So I looked at its parsing code and the printing code.
On parsing side...
- It was using BigInteger because the year portion could be arbitrary big (such as year 99999999999999). But in practice, people only use 4 digit year. Although the internal memory structure has wisely separated this into a BigInteger portion and an int portion, the parsing code was written to always use BigInteger. Eliminating this gave me a good boost.
- It was using String.subtring() to cut a portion of a string, and then Integer.parseInt to get the int value. This creates unnecessary short-lived String instances. Eliminating this gave me a good boost.
On printing side...
- It was using String.valueOf(int) to turn an int to a String, only to append it to StringBuffer. This creates unnecessary short-lived String instances, so I eliminated this.
- StringBuffer turns out to be rather expensive, because of its locking. Changing this to StringBuilder gave me a good boost. Unfortunately, JAXP needs to run in JDK 1.4 where StringBuilder isn't available, so eventually I modified the code to use char directly.
My trust to profilers are really fading these days. For example, profiler didn't point me to any of those optimizations. It's more or less the common sense and trial and error, which led me to those improvements. I guess profilers are good at narrowing down places to look at (especially if you are working on a large codebase), but you shouldn't trust profilers too much.
All in all I was able to make the parsing 150% faster and printing 400% faster. Not a bad gain for a few hours of hacking, don't you think?