Skip the prototype walk for array hole reads when the chain is provably clean#2659
Merged
Merged
Conversation
…ly clean Reading a hole (or any absent index) fell off the dense fast path into a full prototype-chain probe that allocates a JsString per index - a loop summing a 75%-holey array allocated 7 MB per op and the in operator the same. The write fast path already trusts CanUseFastAccess (no exotic own descriptors, prototype identity intact, no index properties placed on Array.prototype/Object.prototype - QuickJS guards its array fast path with the same idea as a single std_array_prototype flag); the read side now trusts it too: Get(uint), the indexed Get(JsValue) miss and HasProperty return undefined/false directly when the invariant holds. Index getters set NonDefaultDataDescriptorUsage, prototype index additions set ObjectChangeFlags.ArrayIndex, and subclass or custom prototypes fail the identity check - all of those fall through to the unchanged walk. New tests pin each escape hatch. ArrayHoleTraversalBenchmark (default job): | Lane | Before | After | |---------------- |---------------------:|------------------:| | SumHoley | 21.03 ms / 7,015.7 KB | 14.50 ms / 1.32 KB | | InOperatorHoley | 16.70 ms / 7,015.7 KB | 8.45 ms / 1.32 KB | | SumDense | 10.44 ms | within noise | | Join/IndexOf | unchanged | unchanged | -31% / -49% time and -99.98% allocation on the holey lanes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lahma
enabled auto-merge (squash)
July 13, 2026 08:57
This was referenced Jul 15, 2026
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.
Fourth PR of the quickjs-ng learnings campaign (#2654 lanes, #2656 for-in array fast path, #2658 guard fusion). QuickJS guards its entire array element fast path — including hole semantics — with a single
std_array_prototypeinvariant flag; Jint already tracks the equivalent invariant (CanUseFastAccess: no exotic own descriptors, prototype identity intact, no index properties placed onArray.prototype/Object.prototype) and trusts it on the write side. This PR extends that trust to the read side.What
Reading a hole (or any absent index) fell off the dense fast path into a full prototype-chain probe that allocates a
JsStringper index — a loop summing a 75%-holey array allocated 7 MB per op, andinover the same array the same. NowGet(uint), the indexedGet(JsValue)miss andHasPropertyreturnundefined/falsedirectly whenCanUseFastAccessholds.Every escape hatch falls through to the unchanged walk, and each is pinned by a new test:
NonDefaultDataDescriptorUsage(viaTrackChanges),Array.prototypeorObject.prototypesetsObjectChangeFlags.ArrayIndexon the prototype,Numbers
ArrayHoleTraversalBenchmark(default job):Gate
Full gate green: Jint.Tests (incl. three new escape-hatch tests), PublicInterface, CommonScripts, and a clean 99,429-test Test262 run — which includes the
Array.prototypeindex-shadowing suites this change must respect.🤖 Generated with Claude Code