perf(core): trim per-keypress waste on the focus and state path - #56
Merged
Conversation
Three behavior-preserving reductions on the path that runs for every
focus change:
- States.has() built a dollar-prefixed template string on every call to
support unprefixed lookups (has('focus') matching '$focus'). The hot
caller is updateFocusPath querying Config.focusStateKey, which is
already prefixed, so the second lookup could only ever match a doubled
prefix that nothing produces. A charCode check now short-circuits it,
and the allocation happens only when an unprefixed query misses.
- updateFocusPath allocated a Set per focus change purely to diff the
previous focus path against the new one. The paths are a handful of
elements and the Set never escapes, so a linear indexOf scan does the
same diff with no allocation or hashing.
- _stateChanged took the style-resolution branch whenever _undoStyles
was set, including the empty array left behind once a state style has
been undone (empty arrays are truthy). With nothing to undo and no
style matching any active state the branch provably assigns nothing,
so it is now skipped in that case.
Measured honestly: on a desktop browser these changes did not move the
per-press benchmark numbers beyond run-to-run noise; an A/B/A sequence
showed environmental drift larger than any effect. They are strictly
less work with identical observable behavior (178 tests pass unchanged),
and the allocation reduction targets GC pressure on TV-class devices, so
a device benchmark run is the gate on whether they actually help there.
Context: the keypress attribution work that motivated looking here found
the focus and state machinery is a small share of a press next to row
construction inside Solid's batched key handler. These are the pieces of
that machinery that were pure waste either way.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
What
Three behavior-preserving reductions on the path that runs for every focus change (so, every navigation keypress):
States.has()no longer allocates on prefixed lookups. It built a`$${state}`template string on every call to let unprefixed queries match (has('focus')matching'$focus'). The hot caller isupdateFocusPathqueryingConfig.focusStateKey, which is already$-prefixed, so that second lookup could only ever match a doubled prefix that nothing produces. AcharCodeAt(0)check now short-circuits it; the allocation happens only when an unprefixed query misses the direct lookup.updateFocusPathdrops its per-pressSet. The Set existed only to diff the previous focus path against the new one, never escaped, and the paths are a handful of elements. A linearindexOfscan does the same diff with no allocation or hashing._stateChangedskips the style-resolution branch when there is provably nothing to do.this._undoStylesis left as an empty array once a state style has been undone, and empty arrays are truthy, so the branch ran forever after that even when no style matched any active state. It now requires a non-empty undo list or a matching state style; every sub-case of the skipped branch assigns nothing.Why
Keypress attribution work on the row-mount jank found the focus/state machinery is a small share of a press next to row construction inside Solid's batched key handler. These are the pieces of that machinery that were pure waste either way, aimed at allocation/GC pressure on TV-class devices.
Measured result, stated plainly
On a desktop browser these changes did not move the per-press benchmark numbers beyond run-to-run noise: an A/B/A sequence (6 benchmark runs per arm) showed environmental drift larger than any effect. They are strictly less work with identical observable behavior, so the recommendation is to gate this on a device benchmark run rather than take it as a claimed win. One semantic edge case to be aware of: a state literally named with a doubled prefix (e.g.
'$$focus') would previously match ahas('$focus')query and no longer does; nothing produces such a state.Verification
pnpm test: 178/178 pass, repeated runspnpm tscclean,pnpm lintclean_stateChangedcall counts per press were bit-identical between baseline and this branch🤖 Generated with Claude Code