You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A profile-driven pass over the VM hot paths, sliced into small independent PRs so each can be judged (and reverted) on its own. Every slice is pure pkg/vm (a couple also touch builtin call sites mechanically), with no intended semantics change. This issue is the map + a reviewer's guide so the review time goes to the few decisions that need judgment, not to re-deriving that the green is real.
byte comparison for BMP </>/<=/>=, skip the []uint16 materialization
low-med
TL;DR
Eight small VM perf PRs. Benchmarks up 30–300% on hot paths; 0 Test262 regressions, measured (not asserted — see below). Each PR is independent and self-contained. Four are low-risk enough to merge on the verification alone; four carry a single design decision worth your eyes. You don't need to read this whole issue — the fast path is enough to act.
Fast path to merge
Trust basis: every commit is build + go vet + full TestScripts green. Every semantics-adjacent slice is additionally diffed against the Test262 language suite as a differential vs a main control — 0 new failures on all of them. Built-ins checked by per-directory sweep — 0 regressions.
Prefer one merge over eight? There's a single branch on the fork, mparrett:perf/vm-microopt-all, with all eight slices stacked in dependency order off main — build + TestScripts + Test262 language all green (0 new failures). Review the pieces here, and if they check out I'll open it as a PR (or you can pull/merge it directly); merging it supersedes the individual PRs. Say the word.
Validates any slice end-to-end. It needs the Test262 corpus, which the repo pins and fetches:
./setup-test262.sh # one-time: clones tc39/test262 at the pinned .test262-rev into ./test262
git fetch origin && git checkout <branch> # e.g. perf/vm-mapset-keys
go build ./... && go test ./tests -run TestScripts
# Test262 language differential vs main (the slices are cut off main):
go build -o /tmp/t262 ./cmd/paserati-test262
./t262 -path ./test262 -subpath language -timeout 0.2s -dump /tmp/slice.dump # this branch
git checkout main && go build -o /tmp/t262 ./cmd/paserati-test262
./t262 -path ./test262 -subpath language -timeout 0.2s -dump /tmp/base.dump # control
# new failures (empty output == clean):
comm -12 <(grep '^-' /tmp/slice.dump | sed 's/^.//' | sort) \
<(grep '^+' /tmp/base.dump | sed 's/^.//' | sort)
(Dump lines are +path for a pass, -path for a fail; a new failure is a test failing on the slice but passing on the control.)
Map/Set per-iterator state moved to a PlainObject.internalIterState pointer (+8B) — those slots were formerly enumerable user-visible props, so this makes them spec-correct internal slots. Deopt on a replaced next.
The Symbol.iterator identity-check that guards the fast path. Instance-level arr[Symbol.iterator] overrides are already ignored by the generic for-of/spread paths — pre-existing gap, filed separately, not introduced here.
The unsafe.StringData/unsafe.String round-trip and the GC-liveness argument (GC scans the unsafe.Pointer field). -race/checkptr + GOGC=1 stress evidence is in the PR.
SameValueZero canonicalization (NaN via canonical bits, ±0 via bits 0), pointer-identity object keys and why they stay GC-safe, the 32B-vs-16B key-size tradeoff.
Why pooling is safe: Call copies args into callee registers and NewArguments deep-copies, so nothing retains the slice. One comma-in-string Call deliberately left un-converted.
Why byte order == UTF-16 order for BMP, and that astral/surrogate content falls back to full UTF-16 so ordering stays correct. Mirrors the existing equality guard.
Notes
Numbers: the local deltas (Apple M2) are directional. The perf-label same-runner A/B on each PR is the gate and supersedes them. A few timeout-bound built-ins tests flip to passing because the VM now finishes them inside the harness limit — the count is machine-sensitive, so it's not claimed as a number.
Conformance: the audit turned up pre-existing inconsistencies (IsCallable vs IsFunction on AsyncNativeFunction; OpIn's accepted-type list; the array Symbol.iterator instance-override gap). All preserved bit-for-bit — none are changed inside a perf PR; I'll file them separately.
A companion arith/dispatch lane (numeric fast paths, IC, dead-code) is staged separately and will follow.
A profile-driven pass over the VM hot paths, sliced into small independent PRs so each can be judged (and reverted) on its own. Every slice is pure
pkg/vm(a couple also touch builtin call sites mechanically), with no intended semantics change. This issue is the map + a reviewer's guide so the review time goes to the few decisions that need judgment, not to re-deriving that the green is real.The PRs
[k,v]=arrreads pristine arrays by indexNewStringstops allocatinginitialized []boolinto a value sentinel (perf-neutral cleanup)SprintfallocCallArgs2/3/4) across 71 builtin sites</>/<=/>=, skip the[]uint16materializationTL;DR
Eight small VM perf PRs. Benchmarks up 30–300% on hot paths; 0 Test262 regressions, measured (not asserted — see below). Each PR is independent and self-contained. Four are low-risk enough to merge on the verification alone; four carry a single design decision worth your eyes. You don't need to read this whole issue — the fast path is enough to act.
Fast path to merge
Trust basis: every commit is build +
go vet+ fullTestScriptsgreen. Every semantics-adjacent slice is additionally diffed against the Test262 language suite as a differential vs amaincontrol — 0 new failures on all of them. Built-ins checked by per-directory sweep — 0 regressions.--delete-branch).Prefer one merge over eight? There's a single branch on the fork,
mparrett:perf/vm-microopt-all, with all eight slices stacked in dependency order offmain— build +TestScripts+ Test262 language all green (0 new failures). Review the pieces here, and if they check out I'll open it as a PR (or you can pull/merge it directly); merging it supersedes the individual PRs. Say the word.Merge mechanics
vm.goregions — no cross-conflicts, merge in any order.main— and if its base was the parent branch, retarget the base tomain. Squash-merging the parent is fine; the child rebase stays trivial.--delete-branchon perf(vm): iterator fast path — step built-in iterators without a call or result object #29 or perf(vm): pool the native-reentry sentinel register slice #33 orphans perf(vm): array-destructuring fast path for pristine arrays #35 / perf(vm): pool callback args slices (CallArgs2/3/4) #36. GitHub usually auto-retargets, but the child still wants a rebase for a clean diff.Optional: hand a slice to an agent
Validates any slice end-to-end. It needs the Test262 corpus, which the repo pins and fetches:
(Dump lines are
+pathfor a pass,-pathfor a fail; a new failure is a test failing on the slice but passing on the control.)Per-PR: what to check
PlainObject.internalIterStatepointer (+8B) — those slots were formerly enumerable user-visible props, so this makes them spec-correct internal slots. Deopt on a replacednext.Symbol.iteratoridentity-check that guards the fast path. Instance-levelarr[Symbol.iterator]overrides are already ignored by the generic for-of/spread paths — pre-existing gap, filed separately, not introduced here.unsafe.StringData/unsafe.Stringround-trip and the GC-liveness argument (GC scans theunsafe.Pointerfield).-race/checkptr + GOGC=1 stress evidence is in the PR.putclears the slot for GC hygiene.Callcopies args into callee registers andNewArgumentsdeep-copies, so nothing retains the slice. One comma-in-stringCalldeliberately left un-converted.Notes
perf-label same-runner A/B on each PR is the gate and supersedes them. A few timeout-bound built-ins tests flip to passing because the VM now finishes them inside the harness limit — the count is machine-sensitive, so it's not claimed as a number.IsCallablevsIsFunctiononAsyncNativeFunction;OpIn's accepted-type list; the arraySymbol.iteratorinstance-override gap). All preserved bit-for-bit — none are changed inside a perf PR; I'll file them separately.