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

compiler: Known hooks/nonescaping scopes dont count as pruned #29820

Merged
merged 3 commits into from
Jun 10, 2024

Conversation

josephsavona
Copy link
Contributor

@josephsavona josephsavona commented Jun 8, 2024

Stack from ghstack (oldest at bottom):

There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:

  • Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
  • Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

[ghstack-poisoned]
Copy link

vercel bot commented Jun 8, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 10, 2024 3:40pm

josephsavona added a commit that referenced this pull request Jun 8, 2024
There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

ghstack-source-id: 75559d25ab3d3af6a330d91e69e4331006aa97c3
Pull Request resolved: #29820
@facebook-github-bot facebook-github-bot added the React Core Team Opened by a member of the React Core Team label Jun 8, 2024
Comment on lines +28 to +35
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (
<div
className={stylex(
flags.feature("feature-name") ? styles.featureNameStyle : null,
)}
/>
);
Copy link
Contributor Author

@josephsavona josephsavona Jun 8, 2024

Choose a reason for hiding this comment

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

This output matches what we have on main today. Whereas with the previous PR in the stack the output was:

const t0 = flags.feature("feature-name") ? styles.featureNameStyle : null;
  let t1;

  if ($[0] !== t0) {
    t1 = stylex(t0);
    $[0] = t0;
    $[1] = t1;
  } else {
    t1 = $[1];
  }

  let t2;

  if ($[2] !== t1) {
    t2 = <div className={t1} />;
    $[2] = t1;
    $[3] = t2;
  } else {
    t2 = $[3];
  }

  return t2;

Way more code and 4 cache slots instead of 1.

@react-sizebot
Copy link

react-sizebot commented Jun 8, 2024

Comparing: a0a435d...8241eec

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.66 kB 6.66 kB = 1.82 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 497.25 kB 497.25 kB = 89.11 kB 89.11 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.67 kB 6.67 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 502.07 kB 502.07 kB = 89.79 kB 89.79 kB
facebook-www/ReactDOM-prod.classic.js = 596.75 kB 596.75 kB = 105.19 kB 105.19 kB
facebook-www/ReactDOM-prod.modern.js = 570.93 kB 570.93 kB = 101.13 kB 101.13 kB
test_utils/ReactAllWarnings.js Deleted 63.89 kB 0.00 kB Deleted 15.97 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
test_utils/ReactAllWarnings.js Deleted 63.89 kB 0.00 kB Deleted 15.97 kB 0.00 kB

Generated by 🚫 dangerJS against 8241eec

@@ -53,12 +53,11 @@ function Component(props) {
x = 1;
}
let t0;
if ($[0] !== x) {
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

x is non-escaping (and therefore counted as pruned) and non-reactive. before this PR, we would have promoted x to reactive because of the pruned+nonreactive combination — now it doesn't count as pruned so we revert back to the output from main

…ned"

There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Jun 8, 2024
There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

ghstack-source-id: b7c36b71aa44a7e4952670b5121c15fe9c96b123
Pull Request resolved: #29820
…ned"

There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Jun 10, 2024
There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

ghstack-source-id: 80610ddafad65eb837d0037e2692dd74bc548088
Pull Request resolved: #29820
@@ -331,29 +331,8 @@ class CountMemoBlockVisitor extends ReactiveFunctionVisitor<void> {
scopeBlock: PrunedReactiveScopeBlock,
state: void
): void {
let isHookOnlyMemoBlock = false;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

this logic is now in FlattenScopesWithHooksOrUse, and also takes account of use() and not just hooks

@josephsavona josephsavona merged commit 8241eec into gh/josephsavona/29/base Jun 10, 2024
54 of 55 checks passed
josephsavona added a commit that referenced this pull request Jun 10, 2024
There are two cases where it's legit/intended to remove scopes, and we can inline the scope rather than reify a "pruned" scope:
* Scopes that contain a single instruction with a hook call. The fact that we create a scope in this case at all is just an artifact of it being simpler to do this and remove the scope later rather than try to avoid creating it in the first place. So for these scopes, we can just inline them.
* Scopes that are provably non-escaping. Removing the scope is an optimization, not a case of us having to prune away something that should be there. So again, its fine to inline in this case.

I found this from syncing the stack internally and looking at differences in compiled output. The latter case was most common but the first case is just an obvious improvement.

ghstack-source-id: 80610ddafad65eb837d0037e2692dd74bc548088
Pull Request resolved: #29820
@josephsavona josephsavona deleted the gh/josephsavona/29/head branch June 10, 2024 15:42
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.

3 participants