Consider copying MultiChildRenderObjectWidget.children #48321
Labels
c: performance
Relates to speed or footprint issues (see "perf:" labels)
c: proposal
A detailed proposal for a change to Flutter
framework
flutter/packages/flutter repository. See also f: labels.
P2
Important issues not at the top of the work list
perf: speed
Performance issues related to (mostly rendering) speed
team-framework
Owned by Framework team
triaged-framework
Triaged by Framework team
One of the problems with the Flutter model is that developers can accidentally or intentionally mutate the Widget tree via mutating MultiChildRenderObjectWidget.children. See dart-lang/sdk#27755.
I think there might be a performance reason to copy the children. It depends on how often the framework walks the
children
.List
is an interface that has many implementations.The VM has
_List
for fixed-length lists and_GrowableList
for 'regular' lists, and another implementation (I don't recall the name) for unmodifiable and constList
s.In a small Flutter app, all the children are likely from
List
literals or to calls to.toList()
, so thechildren
field is likely to have just two or three implementation classes. The JITing VM and AOT try to discover this, and the three implementation classes are carefully crafted to be as alike as possible. This makes it possible to compilechildren[i]
to some indexing code.It is possible that in a large Flutter app that there are other implementations of
List
that are passed in as the children, meaning it is no longer possible to compilechildren[i]
to indexing code since some of the implementations have custom indexers and.length
getters that must be called.My hypothesis is that as the inner loop in the framework becomes more polymorphic, the performance of this code shared by all multi-child widgets degrades.
One could test this hypothesis by taking a benchmark that is where walking the children is 'hot'. I expect the benchmark would walk the 'the same' a large Widget tree over and over, with no actual appearance change. Alter one of the constructors to pass in an
UnmodifiableListView([....])
instead of[....]
. This will cause the walking ofchildren[i]
to become less efficient. Does this show up in the benchmark?If you decide to protect the performance by copying (which ensures there is only one kind of List), I would suggest copying with
List<Widget>.unmodifiable(input)
.A small fixed-length or unmodifiable list can be is half the size of a growable list or less. If the growable input is no longer referenced, it can be GC-ed. The copy will do more allocation, but have a smaller size post-GC. This may be a reasonable tradeoff.
Making the children unmodifiable will prevent users from modifying the trees they create which will help them avoid a class of bugs.
It will break the problematic pattern of
widget.children.add(w)
.Even if the above hypothesis does not pan out, copying the list to an unmodifiable list in debug mode would help developers avoid bugs.
The text was updated successfully, but these errors were encountered: