perf(vm): make arrayIndexAccessorSeen an atomic.Bool [4/4 of #39] - #43
perf(vm): make arrayIndexAccessorSeen an atomic.Bool [4/4 of #39]#43mparrett wants to merge 2 commits into
Conversation
The latch is process-global and written from accessor-definition choke points while OpSetIndex reads it on every indexed write. A plain bool is fail-safe under a race (stale false just keeps the slow path) but still races under -race / concurrent VMs. Use atomic.Bool so publication is well-defined; comment the fail-safe rationale alongside. Co-authored-by: Cursor <cursoragent@cursor.com>
A one-byte bool can't tear, so "torn false→true" named the wrong hazard. The reason for the atomic is the data race itself: -race flags it and the compiler may cache the load across an `arr[i] = v` loop. Also note the ordering covers the flag, not the accessor maps behind it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
This is a substantial regression, not a neutral cleanup. Measured on a dedicated
Alternating base-vs-head launches on No overlap between the two distributions, and each arm is tight (2.9–5.2% spread). This reproduces across every launch. The mechanism is named in the commit message: "nothing stops the compiler caching the load across an Worth considering: the flag is only ever set once and never cleared, and Floors from null controls in the same run: two commits compiling to byte-identical binaries measure up to 3.7% apart on |
|
Correcting my verdict here: the +30.9% is real, and it is not this change. The regressing benchmark is So the un-hoistable-atomic-load reading I gave predicts the opposite ranking to What is left is code layout. This change alters I would leave this open and unmeasured rather than close it as a regression. |
Part 4 of the #39 split — the
arrayIndexAccessorSeenconcern from your review.The latch is now an
atomic.Bool. As you noted it was fail-safe (a stalefalsejust restores the slow path), but it was still a data race under-raceor with concurrent VM instances;Store/Loadmake the publication well-defined.noteAccessorKeykeeps its load-then-store shape rather than a CAS — the only transition isfalse → true, so a redundant store is harmless.go test -race ./pkg/vm/passes.Where this sits in the #39 split
#39 bundled ~6 changes; per review it's split into four independent PRs, all branched off current
main:perf/vm-numeric-compare-rem%, drop unreachable per-op guardsperf/vm-finally-nonallocperf/vm-dispatch-deadcodeIsNaNcleanupperf/vm-array-index-accessor-atomicarrayIndexAccessorSeen→atomic.BoolThe other three pieces you flagged as unmentioned — the
IsObject()range check, theToInteger()fast path, and thearrayIndexAccessorSeenlatch itself — already landed onmainseparately, so they aren't repeated here. Together these four are the remainder of #39.The four merge onto
mainin sequence with no conflicts; the union passesTestScripts,pkg/vm,pkg/compiler, andgo test -race ./pkg/vm/.🤖 Generated with Claude Code