Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Optimization] Reduce unnecessary Component rebuilds #1754

Merged
merged 1 commit into from
Mar 23, 2023

Conversation

nicksenger
Copy link
Contributor

The current implementation of Component results in many unnecessary rebuilds when components are nested inside the view of other components. This can be observed in the following example, with several layers of nested components (code):

unoptimized.mov

This PR optimizes Component to avoid unnecessary rebuilds, after which running the same example results in:

optimized.mov

The cause of excessive rebuilding in the existing implementation is that the component's underlying element is rebuilt eagerly after any potential state mutation. If the component returns a message from update, processing that message at a parent component (or the root application level), will in turn cause another rebuild of the component just built. As a result, with increasing layers of components, the runtime of processing an event which propagates to the root of the application increases in a non-linear fashion.

By deferring rebuild until any widget method is invoked where the latest element is actually required, we can perform this same process in linear time.

If the rebuild is to be deferred until absolutely necessary, invocation of any widget method may require rebuilding of the component's element, and in turn requires diffing of that element's widget sub-tree. This means that mutable access to the element's widget sub-tree is required during every widget method. To achieve this, the widget tree for the component's contained element is moved inside of the component-widget's state and put inside a RefCell. Since width, height and layout do not have even immutable access to the widget tree, but may still be invoked and require rebuilding of the element, an Rc is used to store a reference to the sub-tree on the component-widget instance itself, and populated during diffing (similar to the approach used in the Lazy widget). This allows mutable access to the element's widget tree from any widget method without altering the Widget interface.

For overlay, the contained element's widget sub-tree is moved onto the overlay element, and restored during the overlay's drop (again, similar to how overlay works in the Lazy widget).

This implementation has been tested in a relatively large real-world application having many layers of components both inside & outside overlays. No issues or discrepancies from the current implementation of Component have yet been identified.

@hecrj hecrj added improvement An internal improvement performance widget labels Mar 14, 2023
@hecrj hecrj added this to the 0.9.0 milestone Mar 14, 2023
Copy link
Member

@hecrj hecrj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clever! The lazy crate is getting quite insane. Hopefully we can identify a pattern at some point.

Also, I would generally recommend to avoid nesting Component. A custom widget may work better for containers.

@hecrj hecrj merged commit b74ff9f into iced-rs:master Mar 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants