The most interesting optimization is to replace ArrayDeque with a manual linked list that reuses the nodes 'parent' field. These optimizations save about 20%.
Compared to LinkedTreeMap, this is slower for small (size=5) maps: 124% slower to get() and 33% slower to create and populate. It's a win for large (size=500) maps: 46% faster to get() but 8% slower to create and populate. And it's a big win for very large (size=50,000) maps: 81% faster to get() and 46% faster to create and populate.
http://microbenchmarks.appspot.com/run/limpbizkit@gmail.com/com.google.common.collect.MapBenchmark
I'm going to follow this up with some simple optimizations: caching local fields and simplifying access. That should narrow the performance gap.
Not yet adopted in our code.
Known critical bugs:
- throws ClassCastException when get() is called with a non-comparable key
- throws NullPointerException on get(null)
This makes Hotspot slower. From my before/after measurements using ParseBenchmark, times in microseconds:
TWEETS: 350 -> 370 (+6%)
READER_SHORT: 77 -> 76 (-1%)
READER_LONG: 870 -> 940 (+8%)
But it makes Dalvik faster by a greater margin. These before/after measurements use times in milliseconds:
TWEETS: 25 -> 20 (-20%)
READER_SHORT: 5.6 -> 4.7 (-16%)
READER_LONG: 52 -> 47 (-10%)
It's a net win because we're saving a greater fraction of time, and because we're helping the platform that needs the most help. We're paying microseconds on Hotspot to gain milliseconds on Dalvik.
The motivating difference is that JsonReaderV2 tries to read each character at most once. This means that when it reads literals, it also attempts to decode them to a keyword (true/false/null) or a number.
This change also _doesn't_ read strings until demanded to do so. This should permit streaming access to strings down the road.
This code is not yet complete, nor is has it been properly optimized. And the implementation is also quite a mess! It is a work in progress.
One of our favorite users (my employer!) is stuck in a sad situation where they need to serialize objects returned from Gson; this is a workable escape hatch.
TWEETS
run vm htmlSafe ms linear runtime %
Before app_process true 68.7 ============================== 100%
After app_process true 35.9 =============== 52%
READER_LONG
run vm htmlSafe ms linear runtime %
Before app_process true 439.0 ============================== 100%
After app_process true 74.5 ===== 17%
I'd prefer to not support multiple top-level values, but we support it in JsonReader and it's easier to be consistent. Kevin Hayen's patch pointed me in the right direction here, but I needed to do more work to cover some of the edge cases.
Fixes issue 397.