perf(nanoviews): clear a row range with replaceChildren - #216
Merged
Conversation
`removeBetween` walks a `Range` over everything between the two markers of a period and deletes it. When those markers stand at the ends of their parent, everything between them is everything the parent holds, and the range says the long way what one call says directly. `parent.replaceChildren(start, end)` sets the parent back to its two markers. In `09_clear1k_x8` the script time falls from 34.90 ms to 31.65 ms and the total from 40.10 to 36.60 - 40 iterations per arm, two rounds with the arms alternating, 95% CI [-4.10; -2.60]. That is about half of the gap to the fastest field in that case, and it moves the weighted geometric mean from 1.139 to 1.129. The empty range now behaves too: with nothing between the markers the old path asked for a range from `end` to `start`.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #216 +/- ##
==========================================
+ Coverage 85.36% 85.38% +0.01%
==========================================
Files 140 140
Lines 3151 3154 +3
Branches 593 594 +1
==========================================
+ Hits 2690 2693 +3
Misses 332 332
Partials 129 129 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This was referenced Aug 25, 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.
Clearing a period walks a
Range:startandendare the two text markers a period keeps its place with, so what has to go is everything between them. But when those markers stand at the ends of their parent, everything between them is everything the parent holds — and the range is saying the long way what one call says directly.export function removeBetween(start: Node, end: Node): void { - remove(start.nextSibling!, end.previousSibling!) + const parent = start.parentNode! + + if (start === parent.firstChild && end === parent.lastChild) { + parent.replaceChildren(start as ChildNode, end as ChildNode) + } else { + remove(start.nextSibling!, end.previousSibling!) + } }An empty range behaves now too: with nothing between the markers,
start.nextSiblingisendandend.previousSiblingisstart, so the old path asked for a range that runs backwards.Measured
09_clear1k_x8, 40 iterations per arm, two rounds with the arms alternating so a drift inside the session cannot pass for an effect:Both rounds agree.
02_replace1kwas measured alongside as a control and did not move (−0.55, CI [−1.60; +0.55]) —removeBetweenruns once per period swap there, not on a hot path.The case profile says why the ceiling is where it is: of 31 ms of JS in
09,Range.deleteContentswas 27.98 and the whole reactive teardown of a thousand rows was 2.8. This takes about half of the 6.8 ms gap to vue-vapor; the rest is the difference betweenreplaceChildrenand solid'stextContent = ""plus that teardown, which stays.Weighted geometric mean: 1.139 → 1.129.
Cost
+30 B gzip (7558 → 7588). One pin moves:
Average usage (Gzip)3.8 → 3.85 kB.All publicsstays under its own.The same trick does not carry to
remove: in the tail of a reconcile the new rows are already in front of the old ones, so the parent holds[start, …new, …old, end]and the range being deleted is far from all of the children.Lint,
tsc --noEmitand 127 tests are green.