Skip to content

Add adoptShadow instead of mergePreviousOrigin - #32

Open
kronaemmanuel wants to merge 2 commits into
bryntum:masterfrom
kronaemmanuel:13520-scroll-performance
Open

Add adoptShadow instead of mergePreviousOrigin#32
kronaemmanuel wants to merge 2 commits into
bryntum:masterfrom
kronaemmanuel:13520-scroll-performance

Conversation

@kronaemmanuel

@kronaemmanuel kronaemmanuel commented Sep 1, 2026

Copy link
Copy Markdown

A lot of time in the user's example in bryntum/support#13520 is being spent on the mergePreviousOrigin function. Specifically when we copy all the edges from the committed quark origin to the shadow quark this. Also the latestScope.get(idn) Map.get() call takes some time.

I think a good solution would be to just merge the newly created edges directly onto the committed quark. So i've added a new adoptShadow method whhich does this. It has practically gotten rid of the long tasks that the main thread had to wait for.

Krona Emmanuel and others added 2 commits August 27, 2026 12:28
bryntum/support#13520

When a transaction reads a committed, unchanged identifier, the shadow quark
created for the read carries only the few new dependency edges - yet revision
compaction merged in the opposite direction, copying every historical edge of
the committed quark into the shadow (mergePreviousOrigin, O(all old edges) per
identifier per commit). Project-wide identifiers such as startedTaskScheduling
carry an edge per event (~27,000 in the reported dataset) and were copied
wholesale on every live-update commit; profiling showed this merge as the
hottest function on the main thread during scroll (~68% of long-task time).

Replaced the merge with adoption: compactRevisions now folds the shadow's new
edges into the previous quark (Quark.adoptShadow) and keeps the previous quark
as the scope entry, materializing the same shadow-chain union that
outgoingInTheFutureHelper already performs at traversal time - but in the
O(new edges) direction. Committed state is only touched on the commit path,
after markAndSweep has classified the previous revision unreachable, so
transaction reject, branching (guarded by the existing referenceCount check)
and history retention (guarded by eachReachableRevision) behave exactly as
before.

Measured: compaction time -96-98%, median commit 14.9 -> 5.2ms at 4k events
and 23.4 -> 5.1ms at 10k (flat in dataset size); record and revision-scope
digests byte-identical over 150 commits apart from a constant set of inert
stale edges the merge used to prune (filtered by originId at traversal);
0 long tasks / 0ms blocking in the browser harness at the reported 26,670-event
scale with live updates during dense scroll.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bryntum/support#13520

External review caught a retention gap in adoptShadow: an edge target can
itself be a shadow cleared later in the same compaction pass - not a pure-read
shadow (edge targets are always calculating quarks) but a reader that
recalculated to its previous value, which onQuarkCalculationCompleted converts
into a shadow of that previous quark (sameAsPrevious). Storing such a target
verbatim retained the cleared husk on the heap for as long as the edge
survived; a 5,000-reader probe retained 5,000 cleared quarks.

adoptShadow now resolves each folded edge's target through its origin - the
surviving quark, carrying the same originId, which is all any edge consumer
reads. One property comparison per adopted edge, no lookups, no extra pass.

Measured on the realistic retime workload: husk edges -90%, distinct retained
husk objects 27,686 -> 23,569 - fewer than the mergePreviousOrigin design this
replaced (29,361), which had its own equal-magnitude husk case in the mirror
shape (readers dropping a conditional dependency). Edge sets and record digests
are byte-identical; the residual husk shapes are bounded, shared by both
designs, and asserted as a bound by the retention probe (a zero assertion holds
for neither design).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@kronaemmanuel
kronaemmanuel marked this pull request as draft September 1, 2026 05:36
@kronaemmanuel
kronaemmanuel marked this pull request as ready for review September 3, 2026 06:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants