Live data from Hacker News

The cost of parsing JSON

v8.dev

191–200 of 308 posts

Re: The cost of parsing JSON

#191
post #76
post #67

Earlier quoted context omitted.

There is no way of knowing someone won’t do a.foo = window.alert later though, unless it’s a frozen object

That's also true of an object parsed with JSON.parse

JSON.parse won't parse a function call/literal. Direct injection would.

Re: The cost of parsing JSON

#192
post #130

Earlier quoted context omitted.

Interesting how typescript plays into this - I mean back in the wild old days of plain old JS I would be totally fine with putting a JSON.parse here and there, especially on the hot path. But now with static types - this would totally wreck static type checking. And you would need to spend additional cycles to validate that the data is actually correct. Definitely a change request in the PR. This has to be probably a…

Can JS static typing not evaluate constant expressions to infer types?

TypeScript has great type inference, but there's no way to get it to parse JSON strings at type-checking time.

Re: The cost of parsing JSON

#193

I actually think the previous title of this article which was something about JSON.parse being faster than object instantiation or something like was clearer because in English the cost of something implies that it is a negative, whereas here the performance cost is a benefit relative to another solution with a higher cost. maybe I'm being picky though.

I agree. I was expecting something about protocol buffers or a binary based representation of JSON.

Re: The cost of parsing JSON

#194

Earlier quoted context omitted.

Because JSON.parse blocks the thread it's in, and JS is single threaded [1]. So even if you put it behind a promise, when that promise actually runs, it will block the thread. In essence, using promises (or callbacks or timeouts or anything else like that) allows you to delay the thread-blocking, but once the code hits `JSON.parse`, no other javascript will run until it completes. And since no other javascript will r…

Thank you for the excellent explanation! I think of js entirely from a node.js perspective where I conceptualize it as an async task. Is this also wrong?

Node suffers from the same issues, but it's generally not as noticable in most cases. A similar situation in node would cause the server to not be able to respond to any other requests during the `JSON.parse` execution. But in the Node world, you have more options for how to get around those problems (like load balancing requests among several node processes).

But both server-side and client-side JS use the same system, the event loop. It's basically a message-queue of events that get stacked up, and the JS engine will one at a time grab the oldest event in that queue and process it to completion. Anything "async" will just throw a new event into that queue of events to be processed. The secret sauce is that any IO is done "outside" the JS execution, so other events can be processed while the IO is waiting to complete.

Take a look at this link, or search up the JS event-loop if you want to get a better explanation. It's deceptively simple.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Even...

Re: The cost of parsing JSON

#195
post #124

I'm somewhat surprised that parsing an JS object literal is slower than: tokenizing a string literal, resolving all escapes/unicode encodings/etc, resolving the JSON object, resolving the JSON.parse() function, invoking the function, context-switching to native and then actually parsing the JSON. (Though you can probably do some optimizations, such as treating "JSON.parse" as a keyword if you can be sure nothing tamp…

> However, if that's the case, it sounds like a good candidate for an optimisation for V8: why not speculatively try to parse object literals as JSON and only fall back to JS if this causes an error?

I wouldn't be surprised if the vast majority of object literals in the wild are not valid JSON for one reason or another:

  - Object keys and string literals not quoted with `"`.
  - Non literal/non primitive values.
  - Trailing commas.

Re: The cost of parsing JSON

#197

Earlier quoted context omitted.

Because JSON.parse blocks the thread it's in, and JS is single threaded [1]. So even if you put it behind a promise, when that promise actually runs, it will block the thread. In essence, using promises (or callbacks or timeouts or anything else like that) allows you to delay the thread-blocking, but once the code hits `JSON.parse`, no other javascript will run until it completes. And since no other javascript will r…

Thank you for the excellent explanation! I think of js entirely from a node.js perspective where I conceptualize it as an async task. Is this also wrong?

> Is this also wrong?

Yes, node.js javascript runtime is based on V8, the same that runs in Chrome. Javascript is single threaded so anything that is not I/O bound will block the main thread. If you don't want to block the thread becasue you have long running calculation/parsing task, then you can use worker threads[1]. This will run your task in separate thread and not block the main one.

[1] https://nodejs.org/dist/latest-v12.x/docs/api/worker_threads...

Re: The cost of parsing JSON

#198
post #149
post #131

Earlier quoted context omitted.

The reason why it is special cased is that on pipelined or OoO CPU, xor ax,ax would otherwise be significantly slower than straight mov as xor has dependency on its operand registers. On a similar note on many RISC architectures NOP is actually something like ADD r0, r0, r0 and that too is usually special cased in the hazard stall and result forwarding logic (althought usually the special cased part is “ignore hazard…

Adding the same register to itself and then assigning the value to itself is not really a nop. Why is that instruction forbidden and converted to a nop instead?

Because in some RISC architectures, the first (R0) or last (R31 or whatever is largest register number) is also a 'special' register in that it is hardwired to zero, any reads return zero, and writes are simply discarded.

In this instance, if the register always reads as zero, and can never be changed, then ADD R0,R0,R0 is, in effect, a NOP, so it gets special cased, and doing so avoids having to allocate an additional opcode explicitly to the "NOP" instruction.

Anticipating the question of "why is Rx (0 or 31 or ??) hardwired to zero?", that one is because it is useful for:

1) obtaining a zero without having to perform a load from memory

2) creating additional addressing modes by reusing another existing addressing mode

For #2, if the RISC arch. implements, say, base plus index addressing where two registers are added together to obtain a final address, using R0 as one of the inputs creates a direct addressing mode from the base plus index mode.

So base plus index could be written as load R5, R6+R7. Substituting R0 (assuming it is the hardwired to zero register) for R6 (or R7) results in directly addressing from the value in the other register, converting an 'indexed' addressing mode into a 'direct' mode, without having to add a 'mode select bit' to the actual instruction. The result being that the chip only needed hardware for a single addressing mode, but the programmer has two addressing modes available for their use. If memory serves, the DEC Alpha made large use of tricks like this. The hardware only implemented a small handful of addressing modes (say 4-5) yet the full set of addressing modes exposed to the programmer was two or three times larger due to creative uses of "zero stuffing" into the actual hardware modes.

Re: The cost of parsing JSON

#199

Earlier quoted context omitted.

I find this really interesting, because at some point the absolute performance benefits of `JSON.parse` is overshadowed by the fact that it blocks the main thread. I worked on an app a while ago which would have to parse 50mb+ JSON objects on mobile devices. In some cases (especially on mid-range and low-end devices) it would hang the main thread for a couple seconds! So I ended up using a library called oboe.js [1]…

Would probably break that up similar to how you did in that case as well. Though may use multiple server request (chunks) and/or use a websocket for the data feed. What was the memory overhead for the application?

I don't remember details about memory stuff, it was a few years ago now, but I was pleasantly surprised to see that it wasn't nearly as bad as I first assumed it would be.

And I did originally plan on using something like a websocket, but turns out with some minor changes on the server side we could start streaming data while it was still being gathered, and oboe.js is actually able to start parsing data even while it's still downloading from a normal XHR request, and is designed to be as efficient as possible (so it throws away string data as soon as it's not needed any more).

So there weren't really any additional benefits to be had from using websockets and breaking it up into multiple distinct requests would probably have been slower!

(I just realized I forgot to add a link to oboe.js! But I highly recommend it. It seems it's just gotten better since the last time i've used it)

[1] http://oboejs.com

Post reply on HN