Exploration: TrackedArray #storages Map → Array#28
Draft
Conversation
Per-index storage tags in TrackedArray currently use a Map<number, Tag>. Map.get/set has hash + collision overhead. Array[i] is a direct slot read, several times faster for numeric indices. Isolated microbench (n=1000 sequential read): Map 25.06 µs/iter Array 4.71 µs/iter (−81%) At n=5000: Map 232.98 µs/iter Array 24.34 µs/iter (−90%) Sparse patterns (hole-then-fill, random access) also stayed 5–10× faster than the Map version up to n=1000 — V8's dictionary-mode de-opt concern didn't materialize at realistic sizes. Unlike NVP's version, this PR doesn't add the INDEX_STORAGE_THRESHOLD cap — that would silently lose granular invalidation past index 10000. Array all the way down preserves semantics.
📊 Package size report 0.01%↑
🤖 This report was automatically generated by pkg-size-action |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Exploration PR — change from NVP's emberjs/ember.js#21221.
Replace TrackedArray's
#storages: Map<number, Tag>with#storages: Array<Tag | undefined>. Deliberately without NVP'sINDEX_STORAGE_THRESHOLD = 10000cap — that would silently drop granular invalidation past index 10k.Isolated microbench (
bin/tracked-array-storages.bench.mjs)Krausest tracerbench (80-fid)
The microbench win does NOT translate to Krausest.
Saved to
.bench/c-storages-array-80fid.json.Why the divergence
The microbench simulates the data-structure swap in isolation using simple objects. In the real TrackedArray:
#storagesis accessed through a private class field, which adds a slot lookup V8 handles uniformly for both Map and Array values#dirtyCollection()setsthis.#storages.length = 0on every array mutation — this empties the array but keeps it allocated. Subsequent sequential re-fills trigger ElementsKind transitions that Map doesn't have to paygettrap callsconvertToInt→#readStorageFor→consumeTagper numeric access — the inner storage lookup is a smaller fraction of the trap total than the microbench measured in isolationVerdict
Closing. Microbench says Array should be faster; real-world Krausest says it's a regression. The isolated mechanism doesn't survive contact with the surrounding code's access pattern and V8's actual optimizations.
Good reminder that microbench speedups don't guarantee production speedups — the same lesson in reverse of #6/#7 where microbench showed huge wins that didn't register on Krausest either.