Do not re-set container children that resolve to the same widgets - #61
Merged
Merged
Conversation
The fast renderer skips the update of a widget when its element is the same object as last render and its kwargs resolve to the same values. But it compared the resolved kwargs (elements replaced by widgets) with the element kwargs (still elements), so for every container the compare failed and its children were assigned again. A leaf update next to 300 rows re-set the children of the VBox (~170 us for 301 real widgets), a root update re-set the children of all 300 HBoxes. For an unchanged element whose kwargs hold elements, we now compare the resolved kwargs with the values the widget holds. When they are the same objects, setting them is a no-op, so skipping cannot change what the user sees. A value changed from the frontend is still set back, as before, and there is no per-widget cache that could go stale. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
maartenbreddels
force-pushed
the
perf/skip-same-container-children
branch
from
September 28, 2026 12:47
b5c4ea5 to
e565744
Compare
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.
Why
The fast renderer skips the update of a widget when its element is the same object as in the last render and its kwargs resolve to the same values. But it compared the resolved kwargs (elements replaced by widgets) with the element kwargs (still elements). For a container this compare is always false, so every walked container got its
childrenset again on every update.For example, one leaf update next to 300 rows re-set the children of the VBox. One root update re-set the children of all 300 HBoxes.
What
For an unchanged element whose kwargs hold elements (a container, or
layout=,v_slots=), we compare the resolved kwargs with the values the widget holds now. If they are the same objects, setting them would be a no-op, so we skip it. This change only affects the fast renderer.A first version compared with the kwargs we set last time, kept in a dict per container. Review found that it changed behavior: a value changed from the frontend was no longer set back. Comparing with the live values keeps the old behavior, and needs no cache that could go stale.
Widget updates with real widgets, 300 rows (fast renderer):
Comparing the 302 children with the live value costs ~33 µs. Setting them costs ~670–1900 µs (the timings were measured on a busy machine).
Tests
test_leaf_update_does_not_update_sibling_containers: fails on master (fast renderer).test_same_element_sets_back_changes_from_outside: a value orchildrenchanged from outside is still set back. It passes on master and failed on the first version of this PR.v_slotschild that changes. I broke the compare on purpose (dicts always "same", lists compared by length only), and these tests catch it.REACTON_FAST=0and=1.From #58 (its second commit), split off so it can ship on its own.
🤖 Generated with Claude Code