Uh oh!
There was an error while loading. Please reload this page.
perf: Replace generator-based tree teardown and propagation with an explicit collection pass - #3981
Conversation
4276a39 to
237d786Compare| }, | ||
| includeSelf: true, | ||
| ); | ||
| final buffer = _teardownBuffer.isEmpty ? _teardownBuffer : <Component>[]; |
There was a problem hiding this comment.
is allocating one empty list per component removal that significant? feels like this static cache is introducing potential future concurrency problems or hard to find headaches. seems that major gain here is the non-generator teardown and not the brittle [] cache?
Uh oh!
There was an error while loading. Please reload this page.
707869e to
f9caf7dComparef9caf7d to
ed22345Compareed22345 to
ad280e1Comparead280e1 to
8d97b08Compare8d97b08 to
1b48f55Compare| /// subtrees. This matches the order that | ||
| /// `descendants(reversed: true, includeSelf: true)` would produce, without | ||
| /// allocating generator frames on every removal. | ||
| void _collectTeardown(List<Component> out) { |
There was a problem hiding this comment.
wondering if descendants should just use this implementation, then they 1 stay in sync and 2 get the perf benefit?
c817249 to
933bb82Compare…xplicit collection pass
933bb82 to
13caa12CompareThere was a problem hiding this comment.
LGTM, but it is unfortunate that now we have 3 shapes of traversal (before we had 1)
descendants- still kept, lazy using generators, can stop at any time, pays (apparently) huge cost because generators suck?propagateToChildren- fastest and can stop, but does not collect list_collectDescendants- fast and creates list, but eager
And we are saying that _removecannot use propagateToChildren because of concurrent modifications, but it is not clear to me when that happens.
Description
The removal teardown and
propagateToChildrenwalked the subtree through the recursivedescendantssync* generator, allocating generator frames per tree level on every traversal. The teardown now collects the subtree into a local buffer (same leaves-first order) via_collectDescendantsand iterates that snapshot, sinceonRemovecallbacks may mutate the tree mid-walk.propagateToChildreninstead walks the tree with direct recursion and unwinds as soon as a handler stops propagation, so it neither allocates a buffer nor visits more components than the lazy generator did. Event delivery (deliverToComponents) is routed throughpropagateToChildren, so tap, drag, and keyboard propagation benefit as well.The public
descendants()method keeps its documented lazy semantics, since user code relies on early stopping and live iteration there.Extracted from #3960 so the data-structure change there stands alone (as requested in this comment).
Checklist
docsand added dartdoc comments with///.examplesordocs.Breaking Change?
Related Issues
Relates to #3957