-
Notifications
You must be signed in to change notification settings - Fork 49.9k
[Flight] Optimize Async Stack Collection #33727
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
Merged
Merged
Conversation
This file contains hidden or 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
…ite on a chain of awaits This requires eagerly applying a filter to single await frame. For a lot of internal awaits this still needs to collect a lot but once we've found one we can stop including stacks.
In typical usage we almost always will end up using the stack of the Promise instead if one exists. It's only useful in fairly contrived scenarios where you don't use Promises but even if you don't use Promises in your code then you'll likely end up with a Promise at the root of the component which ends up winning in the end anyway.
5d69c66 to
f2feaa6
Compare
eps1lon
approved these changes
Jul 8, 2025
unstubbable
approved these changes
Jul 8, 2025
This was referenced Jul 8, 2025
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.
We need to optimize the collection of debug info for dev mode. This is an incredibly hot path since it instruments all I/O and Promises in the app.
These optimizations focus primarily on the collection of stack traces. They are expensive to collect because we need to eagerly collect the stacks since they can otherwise cause memory leaks. We also need to do some of the processing of them up front. We also end up only using a few of them in the end but we don't know which ones we'll use.
The first compromise here is that I now only collect the stacks of "awaits" if they were in a specific request's render. In some cases it's useful to collect them even outside of this if they're part of a sequence that started early. I still collect stacks for the created Promises outside of this though which can still provide some context.
The other optimization to awaits, is that since we'll only use the inner most one that had an await directly in userspace, we can stop collecting stacks on a chain of awaits after we find one. This requires a quick filter on a single callsite to determine. Since we now only collect stacks from awaits that belongs to a specific Request we can use that request's specific filter option. Technically this might not be quite correct if that same thing ends up deduped across Requests but that's an edge case.
Additionally, I now stop collecting stack for I/O nodes. They're almost always superseded by the Promise that wraps them anyway. Even if you write mostly Promise free code, you'll likely end up with a Promise at the root of the component eventually anyway and then you end up using its stack anyway. You have to really contort the code to end up with zero Promises at which point it's not very useful anyway. At best it's maybe mostly useful for giving a name to the I/O when the rest is just stuff like
new Promise.However, a possible alternative optimization could be to only collect the stack of spawned I/O and not the stack of Promises. The issue with Promises (not awaits) is that we never know what will end up resolving them in the end when they're created so we have to always eagerly collect stacks. This could be an issue when you have a lot of abstractions that end up not actually be related to I/O at all. The issue with collecting stacks only for I/O is that the actual I/O can be pooled or batched so you end up not having the stack when the conceptual start of each operation within the batch started. Which is why I decided to keep the Promise stack.