-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
[Fabric] Optimize cloning ShadowTree when animating layout props #3369
Merged
Conversation
This file contains 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
tomekzaw
changed the title
Optimize cloning ShadowTree when animating layout props on Fabric
[Fabric] Optimize cloning ShadowTree when animating layout props
Jul 20, 2022
Merged
piaskowyk
approved these changes
Aug 16, 2022
fluiddot
pushed a commit
to wordpress-mobile/react-native-reanimated
that referenced
this pull request
Jun 5, 2023
…tware-mansion#3369) ## Description This PR improves the implementation of ShadowTree cloning algorithm in `NativeReanimatedModule::performOperations`. Previously, `ShadowNode::cloneTree` was called for each update, so effectively the root node was cloned $n$ times. In this PR I've proposed an algorithm for solving this task more optimally. The main idea is that unsealed ShadowNodes can be still updated. In particular, their children can be replaced using `ShadowNode::replaceChild`. Also, freshly cloned ShadowNodes are unsealed, so each ShadowNode can be cloned only once and then updated. One important thing to mention is that `replaceChild` does not update `yogaChild_` field in ShadowNode, so we need to invoke `ShadowNode::updateYogaChildren` manually. This function has $O(n)$ time complexity where $n$ denotes the number of children. In order to avoid calling `updateYogaChildren` multiple number of times for the same ShadowNode, the calls are batched in `std::set`. Since in most use-cases there is no more than ~50 props updates, I've decided to use ordered set implementation with $O(\log{n})$ time complexity of insertion in order to save memory as well as avoid the overhead of calculating hash function. | Before | After | |:-:|:-:| | <img width="180" alt="Before" src="https://user-images.githubusercontent.com/20516055/177736298-0ed230dc-edf2-40a5-9a1d-8400c9f58c39.png"> | <img width="159" alt="After" src="https://user-images.githubusercontent.com/20516055/177736518-fd9eeda6-025c-45f1-a636-b4a0665b0c23.png"> | <details> <summary><strong>Execution time measurements</strong></summary> | nested view levels | chessboard size | old algorithm [μs] | new algorithm [μs] | speedup [x] | |:-:|:-:|:-:|:-:|:-:| | 0 | 10x10 | 4008 | 1326 | 3,0 | | 0 | 20x20 | 15962 | 3620 | 4,4 | | 0 | 30x30 | 50287 | 5939 | 8,5 | | 0 | 40x40 | 131410 | 10669 | 12,3 | | 10 | 10x10 | 105792 | 4058 | 26,1 | | 10 | 20x20 | 388404 | 5935 | 65,4 | | 10 | 30x30 | 879948 | 9334 | 94,3 | | 10 | 40x40 | 1598547 | 15477 | 103,3 | | 50 | 10x10 | 524744 | 11750 | 44,7 | | 50 | 20x20 | 2056805 | 17257 | 119,2 | | 50 | 30x30 | 4657397 | 24588 | 189,4 | | 50 | 40x40 | 8305191 | 35413 | 234,5 | </details> <!-- Description and motivation for this PR. Inlude Fixes #<number> if this is fixing some issue. Fixes # . --> ## Changes - Implemented `ShadowTreeCloner` for optimal cloning ShadowTree when animating layout props on Fabric - Used `ShadowTreeCloner` in `NativeReanimatedModule::performOperations` - Moved creation of `PropsParserContext` to `ShadowTreeCloner` <!-- Please describe things you've changed here, make a **high level** overview, if change is simple you can omit this section. For example: - Added `foo` method which add bouncing animation - Updated `about.md` docs - Added caching in CI builds --> <!-- ## Screenshots / GIFs Here you can add screenshots / GIFs documenting your change. You can add before / after section if you're changing some behavior. ### Before ### After --> ## Test code and steps to reproduce <!-- Please include code that can be used to test this change and short description how this example should work. This snippet should be as minimal as possible and ready to be pasted into editor (don't exclude exports or remove "not important" parts of reproduction example) --> The following examples can be used to test the algorithm: - `WidthExample` - `RefExample` - `ChessboardExample` - `NewestShadowNodesRegistryRemoveExample` ## Checklist - [ ] Included code example that can be used to test this change - [ ] Updated TS types - [ ] Added TS types tests - [ ] Added unit / integration tests - [ ] Updated documentation - [ ] Ensured that CI passes
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.
Description
This PR improves the implementation of ShadowTree cloning algorithm in
NativeReanimatedModule::performOperations
.Previously,$n$ times.
ShadowNode::cloneTree
was called for each update, so effectively the root node was clonedIn this PR I've proposed an algorithm for solving this task more optimally. The main idea is that unsealed ShadowNodes can be still updated. In particular, their children can be replaced using
ShadowNode::replaceChild
. Also, freshly cloned ShadowNodes are unsealed, so each ShadowNode can be cloned only once and then updated.One important thing to mention is that$O(n)$ time complexity where $n$ denotes the number of children. In order to avoid calling $O(\log{n})$ time complexity of insertion in order to save memory as well as avoid the overhead of calculating hash function (as well as to keep the order of updates).
replaceChild
does not updateyogaChild_
field in ShadowNode, so we need to invokeShadowNode::updateYogaChildren
manually. This function hasupdateYogaChildren
multiple number of times for the same ShadowNode, the calls are batched instd::set
. Since in most use-cases there is no more than ~50 props updates, I've decided to use ordered set implementation withExecution time measurements
Changes
ShadowTreeCloner
for optimal cloning ShadowTree when animating layout props on FabricShadowTreeCloner
inNativeReanimatedModule::performOperations
PropsParserContext
toShadowTreeCloner
Test code and steps to reproduce
The following examples can be used to test the algorithm:
WidthExample
RefExample
ChessboardExample
NewestShadowNodesRegistryRemoveExample
Checklist