Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[Fabric] Optimize cloning ShadowTree when animating layout props (sof…
…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
- Loading branch information