Closed Bug 557354 Opened 16 years ago Closed 1 year ago

[meta] Improve JSON performance

Categories

(Core :: JavaScript Engine, defect)

defect

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: Waldo, Unassigned)

References

(Blocks 1 open bug, )

Details

(Keywords: meta, perf)

Attachments

(1 file)

The URL claims there's room to improve on at least some benchmarks.
The JSON parser rewrite should have substantially cured JSON.parse performance differentials. There might be more that can be done, but that bulk should get rough parity at least. JSON.stringify may still need efforts.
Depends on: 589664
I'd like to revive this bug; as discussed during JS Workweek, the performance team is very interested in additional improvements that could be made with respect to JSON parse and stringify.
Someone should look at what rapidjson does differently as it is 4-5x faster for parsing telemetry packets. http://code.google.com/p/rapidjson/wiki/Performance
What do telemetry packets look like, JSON-wise? Looking at the code somewhat, and their list of claimed optimizations that make it fast: * C++ Uses templates, not function pointers and stuff. So do we. The Value class is copyable, but with move semantics, so no reference counting overhead needed. Our GC handles (drive-by pun) that for us. * In-situ vs non-destructive Apparently some of their configurations will parse string data like "\u1234foobar\u3456" directly into string characters in-place to avoid memory overhead. Our strings are immutable and JS, so we can't do that. We *do* work to minimize copying when the string's escape-free, and we copy maximal non-escape sequences of characters. * Memory allocation rapidjson values are LIFO-allocated, cutting down on memory overhead. This means the value has to be all-used relatively immediately, a bit of a limitation -- and only from C++, whereas TelemetryPing.js (are there any other telemetry JSON-consuming bits?) obviously needs JS semantics. * SSE2/SSE4.2 acceleration rapidjson optimizes UTF-8 decoding to skip over JSONWhiteSpace. All our JSON parsing is of 16-bit characters, so that can't apply straightforwardly here. Maybe there's something SIMD-ish to be done in the 16-bit case for us. Unclear. Having 8-bit strings as another string type has been discussed from time to time and would help some here with this. Although, rapidjson claims this is mostly helpful for "prettyprinted" JSON data, and I'd guess telemetry data wouldn't be that, generally. But I could be wrong. * Number parsing speed rapidjson says they don't parse numbers directly to double unless it's needed. They also claim 64-bit number parsing as a *feature*. (This is allowed by the JSON RFC but explicitly forbidden by ECMAScript.) We parse non-fractional/exponent numbers with a faster parser, so we get some of this benefit. We use double arithmetic a little earlier even when we use fast decimal-parsing, so we can improve this -- and bug 722788 has patchwork for this. I should poke that bug and keep it moving. :-) Looking only at this list and only running this stuff in my head ;-) I'm guessing systemic concerns will tend to dominate most of the comparisons here. But I could be wrong. As far as what of that list can be done: the 8-bit/16-bit difference is probably the biggest thing we could do. Number parsing, we can tweak a bit more. SIMD, maybe, but I know *nothing* about what's possible there, so someone else will have to say anything at all about that. (Nicolas says 16-bit SIMD is not a thing, but maybe you just have to be trickier, dunno.) Beyond these things, I'm guessing we want to look at JSON interactions with the engine runtime more than anything else. But yeah, to anticipate the obvious response, somebody should profile to get real answers. :-)
Taras, can you say more about how you measured the speed, exactly? If you're parsing the same JSON many times in a loop, that would probably create lots of garbage and cause us to potentially spend a lot of time in GC.
Flags: needinfo?(taras.mozilla)
(In reply to Till Schneidereit [:till] from comment #5) > Taras, can you say more about how you measured the speed, exactly? If you're > parsing the same JSON many times in a loop, that would probably create lots > of garbage and cause us to potentially spend a lot of time in GC. The tests I ran for my workweek presentation did run them in a loop, yes, but I don't think that we can chalk all of that up to GC; I see numbers that are not too far off even if we only iterate a small number of times. I just re-ran them with a small number of iterations; most of the results were still within a couple of percentage points of the original data. One case produced results that were 30% faster, but even that doesn't come close to the 4-5x difference in question.
https://github.com/tarasglek/jsonbench/blob/master/spidermonkey.js is my test script. Email me and i'll provide you sample telemetry pings. The test parses very similar json, but no 2 packets are identical
Flags: needinfo?(taras.mozilla)
I profiled the sample data Taras provided, the full Instruments trace is attached. Sadly, the call graph is quite mangled: in the majority of cases Instruments wasn't able to figure out the current call stack, pretending that all functions were just called from the top level. I didn't really try to fix that in any way other than to roughly group hot functions by functionality. With that out of the way, here's a run-down of where we spend our time: JSON parsing: 5.5% JSONParser::parse 4.0% JSONParser::readString 2.8% JSONParser::readNumber 2.4% js_strtod_harder 1.7% GetPrefixInteger 0.9% JSONParser::createFinishedObject 0.8% JSONParser::advance 3.0% ~ roughly in sum of long tail stuff Char and string handling: 8.3% js_fgets 4.8% AtomSet::lookup 4.5% InflateString 3.8% AtomizeChars Objects and properties handling: 4.9% DefinePropertyOrElement 3.6% Shape::search 3.5% JSObject::addPropertyInternal 1.6% DefineNativeProperty 0.9% NewObjectWithClassProtoCommon 0.8% JSObject::setLastProperty 4.5% ~ roughly in sum of long tail stuff Allocation: 7.4% ReallocateElements 2.0% JSObject::extendDenseElements 1.7% AllocateElements 0.8% JS_realloc 1.5% ~ roughly in sum of long tail stuff Types: 1.0% TypeCompartment::newTypedObject 0.9% TypeCompartment::fixObjectType 0.8% ObjectTypeTable::lookup 0.7% TypeObject::getProperty 1.5% ~ roughly in sum of long tail stuff GC: 11% various (but we do that in another thread, so it shouldn't affect the wallclock time too much). So, based on that, I guess optimizing the JSON *parsing* itself will fairly quickly run into diminishing returns, but it might be good for a few percentage points. Whitespace handling via clever SIMD stuff won't gain us anything at all, at least not for the types of JSON data we care about. Given how much time we spend dealing with strings and allocation, the biggest gains would probably be realized by implementing support for narrow strings as V8 has. That is a huge undertaking, however, so certainly not a near-term fix. GGC should help with the allocations, I suppose. Maybe something clever could be done with the way we're creating our objects, but nothing comes to mind, atm. In any case, I feel woefully out of my depth in analyzing this, so maybe people smarter and more knowledgeable than me can take a look at this data.
Assignee: general → nobody
Summary: Improve JSON performance → [meta] Improve JSON performance
Severity: normal → S3

Closing this bug. New bugs have since been filed and fixed to improve JSON performance. Any new bugs are unlikely to be tagged to this bug. Hence closing this.

Status: NEW → RESOLVED
Closed: 1 year ago
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: