Skip to content

JsonValue: an object of 16 members or more finds a member through a table - #97

Merged
ASDAlexander77 merged 1 commit into
mainfrom
json-object-key-index
Nov 14, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
json-object-key-index

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Follow-up to #96, which left this one out.

What

Every get, has and set on a JsonValue object looked through all its names, so reading an object of n members cost n * n. An object of 16 members or more now finds a member through a hash table.

One object Before After
2,000 keys, JSON.parse 5 ms 1 ms
8,000 keys, JSON.parse 62 ms 5 ms
32,000 keys, JSON.parse 1072 ms 17 ms
32,000 keys, about 4,600 get calls 135 ms under 1 ms

Measured under the release JIT (gc, Windows x64).

How

  • JsonValue gets private slots: int[]: an open-addressed table of name positions, hashed with HashHelpers.hashCodeString.
  • It is made when an object reaches JSON_INDEX_FROM (16) members and remade, twice the size, when half full. That happens in set and in clone, so a lookup only reads it.
  • Smaller objects are looked through as before, and the field is an empty array for them.
  • Not a Map: one built into the library would be built again into a program that uses the same Map type.
  • lib.d.ts declares the new private field, since a program reads the class layout from there.

For the reviewer

  • Cost: every JsonValue carries one more field, and parsing many small objects is about 10% slower (80,000 four-key objects: 184 ms before, 202 ms after). Objects of 24 keys came out about level.
  • Layout change: JsonValue grows by one field, so the library and lib.d.ts must be updated together.

Testing

  • New test tests/json_many_keys.ts: sizes around the 16-member threshold and up to 5,000 keys, duplicate keys, set on existing and new keys, key order, clone, and a stringify round trip.
  • Release, Windows x64: the whole suite passes for gc, rc, none and own, compiled and under the JIT.
  • scripts/check_declarations.ps1 passes.
  • Not run locally: debug builds, Linux, x86 and wasm.

🤖 Generated with Claude Code

…able

Every get, has and set looked through all the names, so reading an object of
n members cost n * n: JSON.parse of one object of 32,000 keys took 1072 ms.
It now takes 17 ms (8,000 keys: 62 ms to 5 ms).

The table is an open-addressed int[] of name positions inside JsonValue,
hashed with HashHelpers.hashCodeString. It is made when an object reaches
JSON_INDEX_FROM (16) members and remade, twice the size, when half full, in
set and in clone, so a lookup only reads it. Smaller objects are looked
through as before. Not a Map: one built into the library would be built
again into a program that uses the same Map.

lib.d.ts declares the new private field, as it does the others.

Cost: every JsonValue carries one more field, and parsing many small objects
is about 10% slower (80,000 four-key objects: 184 ms to 202 ms).

Test: json_many_keys.ts. Tested in release on Windows x64 for gc, rc, none
and own, compiled and under the JIT; debug, Linux, x86 and wasm were not
built locally.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 56c4dc9 into main Nov 14, 2026
10 checks passed
@ASDAlexander77
ASDAlexander77 deleted the json-object-key-index branch November 14, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant