Use bitstack for depth array
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 24.3k
- Forks
- 1.3k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 16
Description
Right now we maintain a stack of booleans to find out whether we are in an array or an object. This means that on every [ { } ] we incur a write or read. It is generally only used to decide where to branch; so it's only relevant when the branch might be mispredicted (particularly on read). This happens in the scope_end label, and means a 3 cycle delay before we know if the branch is wrong. We could save space by storing these as bits instead of bytes, but size isn't the problem.
We could, however, store it as bits and keep the top 32 or 64 bits of the bit stack in a register, making the check very fast (<< 1 | is_array to push, >> 1 and check the overflow bit to pop). We can steal the register from depth, in fact: when it's needed, you can calculate depth by keeping a 1 "marker bit" at the top of the bitstack: depth is then 32 - leading_zeroes(bitstack) - 1. We can check if we have exceeded the register's capacity if the << 1 carries the high bit out or if the >> 1 yields 0 (because we shifted out the high bit).
When you start a JSON array, I'd imagine assembly like this:
shl <BitStack>, 1 ; push
jc store_stack ; Size exceeded: push <BitStack> into memory and repeat.
or <BitStack>, 1 ; is_array = true
When you pop, I'd imagine assembly like this:
shr <BitStack>, 1 ; pop and check the parent
jz read_stack ; pop memory into <BitStack> and repeat. If nothing left to read, goes to document_end.
jc array_continue ; if the bottom bit was 1 (is_array = true), we're in an array
jmp object_continue ; if the bottom bit was 0 (is_array = false), we're in an object
This also has the nice property that you only have to check for depth == 0 and max_depth when you run out of bits. 99% of JSON files have shallow nesting and will only ever have to check depth == 0 once, at the very end. The overflow branches are 100% predictable for most files, therefore.
Does this data structure exist that anyone knows of? I was trying to find it but couldn't see anything. I also haven't figured out how to make C++ generate the above assembly. It particularly doesn't have a way to give you the overflow, and the naive "read the bit I want before shifting it out" didn't produce the assembly I wanted.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by locating the boolean depth stack and the scope_end label mentioned in the issue, then inspect how depth and array/object branching are currently maintained. Compare a register-backed bit stack with the existing implementation and verify the generated assembly and parser behavior; done means the proposed optimization works without breaking nesting or depth handling.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- performance
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100