Performance gains with JSON.parseBinary and ArrayBuffer.prototype.detach #865
Guthib-of-Dan
started this conversation in
Ideas
Replies: 1 comment
-
|
Hello again, @panva. So what are your thoughts on this? |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Hello. I have created 2 TC39 proposals (currently stage 0) to address problematic handling of raw data, coming from internet - JSON.parseBinary and ArrayBuffer.prototype.detach. Currently, I am trying to find support for my ideas and look for projects which they can contribute to.
Existing problems
In
josesource code there are many occurences of TextDecoder, followed by JSON.parse + try-catch block. I have done thorough benchmarking of these tools and results suggest the following:JSON.parse emits
SyntaxError, which includes stack trace for debugging. Question is: why debug code that parsed payload, which is not even ours? Errors have meaning solely for debugging, not for production-level code. Injosecodebase eachSyntaxErrorobject after JSON.parse is left unused. Furthermore, if it was to return an object{ ok: true, value: object } | { ok: false, message: string }then it would execute "450X - 500X" faster without creating "SyntaxError" and its stack trace.We can avoid stack trace manually by hacky
Error.stackTraceLimit = 0, but performance degrades even with simple "throw" keyword and "try-catch" blocks, which have no workarounds.josedeals with invalid payloads frequently, as no one can enforce client to send only right payloads, that is why it is important to optimise for this frequent scenario.To parse JSON
joseneeds to convert it from binary to JS intermediate string.First of all, that requires keeping "TextDecoder" globally and having to deal with import/export or create "new TextDecoder" each time, which adds pressure to Garbage Collector.
Secondly, Garbage Collector is lazy and those intermediate strings may stay in memory for a very long time until GC is forced to do "stop-the-world" cleaning. Benchmark shows that slowdown can reach 20%.
Lastly, if JSON.parse fails (possible and routine scenario, mentioned above) then this string is a waste of CPU time, memory and ... memory again, because GC is lazy and keeps it for a while.
josedeals with Base64 encoding/decoding which results in temporary buffers. Same story as with intermediate strings - they influence lazy GC and lead to slowdowns. Benchmark demonstrates ~30% reduced latency of http requests that clear unused ArrayBuffers.Conclusion
JSON.parseBinarytakes binary payload, avoding intermediate string and unwanted GC pressure, decodes and parses at the same time, so all invalidity is spotted with minimal preceding actions.ArrayBuffer.prototype.detach"detaches" backing store of ArrayBuffer (clears that memory), helping with asynchronous flow of JS runtime (many http requests = load of decoding = many simultaneously alive buffers, yet to be unpredictably cleaned by GC).what changes in this line of code for instance
What is your opinion, @panva ? These proposals can improve overall performance of the library, and each my word has documented proofs - benchmarks in GitHub Actions of both repos.
Beta Was this translation helpful? Give feedback.
All reactions