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

Traverse commit phase effects iteratively #20094

Merged
merged 2 commits into from
Oct 27, 2020

Conversation

acdlite
Copy link
Collaborator

@acdlite acdlite commented Oct 26, 2020

Initial implementation done. It's in a working state, but the diff is large so I want to do a tidying pass and go through it again line-by-line. Maybe remove some duplication.

Would appreciate a thorough review. It's a lot of copy-paste-tweak, so it's likely I missed something.

Description

We suspect that using the JS stack to traverse through the tree in the commit phase is slower than traversing iteratively. This changes the commit phase to use while loops and the fiber return pointer, instead of recursion.

There are a few places in the commit phase where we'd like to read data from the stack, e.g. to check if we're inside a hidden tree. In the render phase, we maintain a virtual "stack" on the heap. We could do that in the commit phase, too, but since the commit phase is synchronous, and most nodes do not need access to the stack, I've chosen to continue using the JS stack (recursion) whenever we hit a node that "provides" data to children. This is pretty rare, though, so it shouldn't have too much of an impact; we were already using this hybrid recursive-iterative strategy in hideOrUnhideAllChildren.

I've kept the recursive implementation behind a flag, both so we have the option to run an experiment comparing the two, and so we can revert it easily later if needed.

To Do

  • Move rest of commit phase logic ReactFiberCommitWork
  • Snapshot phase
  • Mutation phase
  • Layout phase
  • Passive phase
  • Tidy up

The current traversal logic is spread between ReactFiberWorkLoop and
ReactFiberCommitWork, and it's a bit awkward, especially when
refactoring. Idk the ideal module structure, so for now I'd rather keep
it all in one file.
We suspect that using the JS stack to traverse through the tree in the
commit phase is slower than traversing iteratively.

I've kept the recursive implementation behind a flag, both so we have
the option to run an experiment comparing the two, and so we can revert
it easily later if needed.
@facebook-github-bot facebook-github-bot added CLA Signed React Core Team Opened by a member of the React Core Team labels Oct 26, 2020
@codesandbox-ci
Copy link

codesandbox-ci bot commented Oct 26, 2020

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 368930d:

Sandbox Source
React Configuration

@sizebot
Copy link

sizebot commented Oct 26, 2020

Details of bundled changes.

Comparing: 4e5d7fa...368930d

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% 0.0% 908.22 KB 908.22 KB 206.3 KB 206.3 KB NODE_DEV
ReactDOMForked-prod.js 🔺+0.9% 🔺+0.9% 372.58 KB 375.89 KB 68.99 KB 69.63 KB FB_WWW_PROD
react-dom-server.node.development.js 0.0% 0.0% 138.57 KB 138.57 KB 36.58 KB 36.58 KB NODE_DEV
react-dom.production.min.js 0.0% 0.0% 122.29 KB 122.29 KB 39.3 KB 39.3 KB NODE_PROD
ReactDOMForked-profiling.js +1.1% +0.9% 387.75 KB 391.97 KB 71.63 KB 72.3 KB FB_WWW_PROFILING
react-dom-server.browser.development.js 0.0% 0.0% 144.72 KB 144.72 KB 36.76 KB 36.76 KB UMD_DEV
react-dom-server.node.production.min.js 0.0% 0.0% 20.66 KB 20.66 KB 7.65 KB 7.65 KB NODE_PROD
react-dom-test-utils.production.min.js 0.0% 0.0% 13.71 KB 13.71 KB 5.32 KB 5.32 KB UMD_PROD
ReactDOMTesting-dev.js 0.0% 0.0% 912.39 KB 912.39 KB 205.23 KB 205.23 KB FB_WWW_DEV
react-dom-test-utils.development.js 0.0% 0.0% 66.28 KB 66.28 KB 18.84 KB 18.84 KB NODE_DEV
react-dom-unstable-fizz.node.development.js 0.0% +0.1% 5.52 KB 5.52 KB 1.84 KB 1.84 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 13.7 KB 13.7 KB 5.26 KB 5.27 KB NODE_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 5.25 KB 5.25 KB 1.78 KB 1.78 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.1% 1.22 KB 1.22 KB 712 B 713 B UMD_PROD
react-dom-unstable-fizz.browser.development.js 0.0% +0.1% 4.78 KB 4.78 KB 1.68 KB 1.68 KB NODE_DEV
react-dom.development.js 0.0% 0.0% 954.39 KB 954.39 KB 208.87 KB 208.87 KB UMD_DEV
react-dom-unstable-fizz.browser.production.min.js 0.0% 🔺+0.2% 1.01 KB 1.01 KB 616 B 617 B NODE_PROD
react-dom.production.min.js 0.0% 0.0% 122.12 KB 122.12 KB 40.02 KB 40.03 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 127.38 KB 127.38 KB 41.65 KB 41.65 KB UMD_PROFILING
ReactDOMForked-dev.js +0.3% +0.1% 966.93 KB 970.3 KB 214.85 KB 215.08 KB FB_WWW_DEV
react-dom.profiling.min.js 0.0% 0.0% 127.73 KB 127.73 KB 40.95 KB 40.96 KB NODE_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 20.33 KB 20.33 KB 7.55 KB 7.55 KB UMD_PROD
ReactDOM-dev.js -0.0% -0.0% 955.24 KB 955.16 KB 213.35 KB 213.31 KB FB_WWW_DEV
ReactDOM-prod.js 0.0% 0.0% 370.21 KB 370.21 KB 68.54 KB 68.54 KB FB_WWW_PROD
react-dom-server.browser.development.js 0.0% 0.0% 137.3 KB 137.3 KB 36.33 KB 36.33 KB NODE_DEV
react-dom-server.browser.production.min.js 0.0% 0.0% 20.23 KB 20.23 KB 7.49 KB 7.5 KB NODE_PROD
ReactDOMServer-dev.js 0.0% 0.0% 141.48 KB 141.48 KB 36.28 KB 36.28 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% 0.0% 46.43 KB 46.43 KB 10.82 KB 10.82 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% 0.0% 71.47 KB 71.47 KB 19.35 KB 19.35 KB UMD_DEV

ReactDOM: size: 0.0%, gzip: 0.0%

Size changes (experimental)

Generated by 🚫 dangerJS against 368930d

@sizebot
Copy link

sizebot commented Oct 26, 2020

Details of bundled changes.

Comparing: 4e5d7fa...368930d

react-dom

File Filesize Diff Gzip Diff Prev Size Current Size Prev Gzip Current Gzip ENV
react-dom.development.js 0.0% 0.0% 872.67 KB 872.67 KB 199.75 KB 199.75 KB NODE_DEV
ReactDOMForked-prod.js 🔺+0.9% 🔺+0.8% 383.9 KB 387.21 KB 70.83 KB 71.42 KB FB_WWW_PROD
react-dom-server.node.development.js 0.0% 0.0% 137.05 KB 137.05 KB 36.37 KB 36.37 KB NODE_DEV
ReactDOMForked-profiling.js +1.1% +0.8% 399.12 KB 403.35 KB 73.46 KB 74.09 KB FB_WWW_PROFILING
react-dom-server.browser.development.js 0.0% 0.0% 143.13 KB 143.13 KB 36.57 KB 36.57 KB UMD_DEV
react-dom-server.node.production.min.js 0.0% 0.0% 20.2 KB 20.2 KB 7.58 KB 7.58 KB NODE_PROD
react-dom-test-utils.production.min.js 0.0% 0.0% 13.7 KB 13.7 KB 5.31 KB 5.32 KB UMD_PROD
ReactDOMTesting-dev.js 0.0% 0.0% 940.73 KB 940.73 KB 210.75 KB 210.75 KB FB_WWW_DEV
react-dom-test-utils.development.js 0.0% 0.0% 66.27 KB 66.27 KB 18.83 KB 18.83 KB NODE_DEV
react-dom-test-utils.production.min.js 0.0% 0.0% 13.68 KB 13.68 KB 5.26 KB 5.26 KB NODE_PROD
react-dom.development.js 0.0% 0.0% 917.09 KB 917.09 KB 202.29 KB 202.29 KB UMD_DEV
react-dom.production.min.js 0.0% 0.0% 117.65 KB 117.65 KB 38.64 KB 38.64 KB UMD_PROD
react-dom.profiling.min.js 0.0% 0.0% 121.55 KB 121.55 KB 39.85 KB 39.85 KB UMD_PROFILING
ReactDOMForked-dev.js +0.3% +0.1% 992.51 KB 995.88 KB 219.62 KB 219.86 KB FB_WWW_DEV
react-dom.profiling.min.js 0.0% 0.0% 121.83 KB 121.83 KB 39.13 KB 39.13 KB NODE_PROFILING
ReactDOM-dev.js -0.0% -0.0% 980.82 KB 980.74 KB 218.04 KB 218.01 KB FB_WWW_DEV
react-dom-server.browser.development.js 0.0% 0.0% 135.79 KB 135.79 KB 36.12 KB 36.12 KB NODE_DEV
ReactDOM-profiling.js 0.0% 0.0% 395.14 KB 395.14 KB 72.79 KB 72.79 KB FB_WWW_PROFILING
react-dom-server.browser.production.min.js 0.0% 0.0% 19.77 KB 19.77 KB 7.42 KB 7.42 KB NODE_PROD
ReactDOMServer-dev.js 0.0% 0.0% 145.51 KB 145.51 KB 37.29 KB 37.29 KB FB_WWW_DEV
ReactDOMServer-prod.js 0.0% -0.0% 47.29 KB 47.29 KB 11.03 KB 11.03 KB FB_WWW_PROD
react-dom-test-utils.development.js 0.0% 0.0% 71.46 KB 71.46 KB 19.34 KB 19.34 KB UMD_DEV

ReactDOM: size: 0.0%, gzip: 0.0%

Size changes (stable)

Generated by 🚫 dangerJS against 368930d

@bvaughn bvaughn self-requested a review October 26, 2020 14:15
@acdlite acdlite marked this pull request as ready for review October 26, 2020 16:59
@bvaughn
Copy link
Contributor

bvaughn commented Oct 26, 2020

Looks like another build variant the reconciler benchmark might want to compare in the short term, @rickhanlonii.

Copy link
Member

@rickhanlonii rickhanlonii left a comment

Choose a reason for hiding this comment

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

The algos and overall strategy look good to me 👍

Will leave the detailed review to @bvaughn, who has context.

@acdlite
Copy link
Collaborator Author

acdlite commented Oct 27, 2020

@bvaughn Ok this is ready for you to look at

Copy link
Contributor

@bvaughn bvaughn left a comment

Choose a reason for hiding this comment

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

This change looks good. The new iterative methods that have to account for Profiler (and soon Offscreen) are a lot less readable in the iterative forks though :(

We aren't actually running any tests again the old recursive methods anymore, are we? That seems potentially problematic.

let child = firstChild;
while (child !== null) {
nextEffect = child;
iterativelyCommitLayoutEffects_begin(child, finishedRoot);
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like a reasonable approach 👍

}
nextEffect = finishedWork;

if ((finishedWork.flags & LayoutMask) !== NoFlags) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't have any practical implications, but we check LayoutMask in the iterative branch and Update | Callback in the recursive branch. Might be worth updating the latter to also check LayoutMask.

@acdlite
Copy link
Collaborator Author

acdlite commented Oct 27, 2020

We aren't actually running any tests again the old recursive methods anymore, are we? That seems potentially problematic.

That's right, since the new reconciler is only enabled in the www variant build. I did turn it on locally to manually test, which can continue to do until the new reconciler is merged into the old. Then we can use __VARIANT__.

@acdlite acdlite merged commit 25b18d3 into facebook:master Oct 27, 2020
@@ -84,6 +84,8 @@ export const enableDiscreteEventFlushingChange = true;
// to the correct value.
export const enableNewReconciler = __VARIANT__;

export const enableRecursiveCommitTraversal = false;
Copy link
Member

Choose a reason for hiding this comment

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

Should this be either true or in the dynamic values?

Copy link
Contributor

Choose a reason for hiding this comment

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

See this comment #20094 (comment)

Andrew's plan was to split iterative and recursive between old and new forks (once we merge the new fork into the old). For now, we're turning on iterative only for the new build (to see if that fixes the top line metrics).

Does make me a little uneasy that we don't have automated tests covering the new + recursive combination, but if we're able to merge new -> old soon, hopefully it's ok?

Copy link
Member

Choose a reason for hiding this comment

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

Yeah ignore me, I'm a clown. This forces iterative mode which is what we want.

Copy link
Contributor

Choose a reason for hiding this comment

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

Nah, we're both clowns. I also had the same initial thought, b'c intuitively I think of the feature flag enabling the new thing :)

@maraisr
Copy link

maraisr commented Oct 27, 2020

In layman terms, what does this PR mean? Ya'll doing some exciting things!

@bvaughn
Copy link
Contributor

bvaughn commented Oct 27, 2020

PR = pull request

@rickhanlonii
Copy link
Member

@maraisr this PR reworks the algorithms that fire effects and mutations in the commit phase to test a theory for why perf regressed in one of our algorithms that hasn't been released yet. These changes don't have any user-facing impact, and is just part of our research for now.

@maraisr
Copy link

maraisr commented Oct 28, 2020

Still mega interesting though!

facebook-github-bot pushed a commit to facebook/react-native that referenced this pull request Nov 9, 2020
Summary:
Base sync before adding Flight files.

This sync includes the following changes:
- **[454c2211c](facebook/react@454c2211c )**: Refactor SchedulerHostConfigs ([#20025](facebook/react#20025)) //<Ricky>//
- **[56e9feead](facebook/react@56e9feead )**: Remove Blocks ([#20138](facebook/react#20138)) //<Sebastian Markbåge>//
- **[3fbd47b86](facebook/react@3fbd47b86 )**: Serialize pending server components by reference (lazy component) ([#20137](facebook/react#20137)) //<Sebastian Markbåge>//
- **[930ce7c15](facebook/react@930ce7c15 )**: Allow values to be encoded by "reference" to a value rather than the value itself ([#20136](facebook/react#20136)) //<Sebastian Markbåge>//
- **[39eb6d176](facebook/react@39eb6d176 )**: Rename ([#20134](facebook/react#20134)) //<Sebastian Markbåge>//
- **[ffd842335](facebook/react@ffd842335 )**: [Flight] Add support for Module References in transport protocol ([#20121](facebook/react#20121)) //<Sebastian Markbåge>//
- **[343d7a4a7](facebook/react@343d7a4a7 )**: Fast Refresh: Don't block DevTools commit hook ([#20129](facebook/react#20129)) //<Brian Vaughn>//
- **[779a472b0](facebook/react@779a472b0 )**: Prevent inlining into recursive commit functions ([#20105](facebook/react#20105)) //<Andrew Clark>//
- **[25b18d31c](facebook/react@25b18d31c )**: Traverse commit phase effects iteratively ([#20094](facebook/react#20094)) //<Andrew Clark>//

Changelog:
[General][Changed] - React Native sync for revisions 4e5d7fa...454c221

Reviewed By: rickhanlonii

Differential Revision: D24698701

fbshipit-source-id: dfaf692b1051150355dece1657764a484b7ae603
koto pushed a commit to koto/react that referenced this pull request Jun 15, 2021
* Move traversal logic to ReactFiberCommitWork

The current traversal logic is spread between ReactFiberWorkLoop and
ReactFiberCommitWork, and it's a bit awkward, especially when
refactoring. Idk the ideal module structure, so for now I'd rather keep
it all in one file.

* Traverse commit phase effects iteratively

We suspect that using the JS stack to traverse through the tree in the
commit phase is slower than traversing iteratively.

I've kept the recursive implementation behind a flag, both so we have
the option to run an experiment comparing the two, and so we can revert
it easily later if needed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants