Repository navigation
Conversation
🦋 Changeset detectedLatest commit: d190aa7 The changes in this PR will be included in the next version bump. This PR includes changesets to release 15 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Merging this PR will improve performance by 13.48%3 warnings
Performance Changes
Tip Curious why performance improved? Comment Comparing Footnotes
|
# Conflicts: # scripts/size/scenarios.js
# Conflicts: # scripts/size/scenarios.js
Summary
Every store setter call allocated at least two Sets:
writeScopes = new Set()for the write scope, and a written-keys Set per record it touched intouch(). Most setters write one store and one key, so on the single-key write path these Sets were most of the per-call garbage.writeScopenow holds the setter's scope in a plain variable. ThewriteScopesSet is created only when a child proxy from another store joins the draft (draftServe).inDraft, the get trap and the shallow-view check readwriteScopefirst.t.wknow holds a bare key for a setter's first write to a record. A second key creates the Set. When a later setter in the same batch writes the record, the chain tailucan be that bare key. The three readers that walk written keys handle both forms:overlayRebuilds, the privatize branch offoldTarget, andnotifyWrites(which passes the list on toreportReplacedContainers).length, so they still create the Set. It is seeded with.add().add()instead of an array literal, so no temporary array is allocated.I also tried swapping
pendingNotifywith a spare Set instead of spreading it into an array and clearing it. I left it out: it cost 40 B minified for a 2–4% gain, because V8'sSet.clear()allocates a new backing table anyway.Numbers
Local prod builds of
@solidjs/signals, next (456e6d5) and this branch loaded in one process and run interleaved, 5 rounds, median ms per iteration. The machine was under load from other jobs, so the base-vs-base column shows the noise level.An earlier run on the same base gave write-floor −21.5%, input burst −8.8% and selection toggle −2.7%. The selection toggle difference is within noise.
Size
node size.mjs --no-gate --jsonfor head and base, thennode gate.mjs head.json base.json, measured after merging next at ab29345. Four scenarios that include stores grew. None is a frozen floor, so this PR raises their caps and recorded minified sizes, with a ledger line inscenarios.jsfor each.signals: + createStoreapp: hydrating + every store primitive familyapp: compiled CSRapp: compiled hydratingEach new cap is the measured brotli size plus 10 B, rounded up to 0.01 KB, and each recorded minified size is the measured one. The base measured locally matches what next recorded in #4645. With the new caps, the gate passes.
How did you test this change?
pnpm build, thennpx vitest runinpackages/signals: 466 files, 6372 tests pass.tscreports no errors insrcor in the new test.tests/store/written-key-chain-privatize.test.ts, covers the privatize branch of the fold when the written-key chain ends in a bare key. To reach that branch, the descendant has to be written first in the batch so that its fold path-copies the row. Both cases fail ifwrittenListdrops bare keys.createStore.test.ts"wrapped nested class", fails if the joined-scope Set is never created, so thewriteScopespath is covered.npx vitest runinpackages/web: 236 files, 1669 tests pass.🤖 Generated with Claude Code