-
-
Notifications
You must be signed in to change notification settings - Fork 6.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
perf: async fs calls in resolve and package handling #15211
Conversation
Run & review this pull request in StackBlitz Codeflow. |
This reverts commit 06462a8.
I don't know why Windows isn't happy (at least in CI) when using This makes this PR a bit more controversial, because it will mean a small regression on Windows due to the extra promises being generated. But a lot of them are needed due to the If someone with more Windows knowledge finds a way to make |
This reverts commit cafe433.
@userquin tested in his Windows machine, and d3c8f90 is working for him with the same node version as CI. @sapphi-red, maybe you could try too when you have some time? Even if it works, I don't think we can just fallback to sync for CI 🤔 |
I found a bug in Node (nodejs/node#51031) and maybe that's the culprit. I pushed a commit to use |
Woah! Amazing find @sapphi-red 👏🏼 ❤️ I'm curious to see if this PR improves the benchmarks on your machine. I don't know why there is so much variance when I measure on mine. |
Here's the results of https://github.com/yyx990803/vite-dev-server-perf on my Windows machine. It's seems on a par. before
after
I'm not sure why the "before" run is faster than the one in #15195 (comment) though 🤔 . |
if ( | ||
(res = tryResolveRealFile( | ||
fileName + fileExt.replace('js', 'ts'), | ||
preserveSymlinks, | ||
)) | ||
) | ||
return res | ||
if ((res = tryResolveFile(fileName + fileExt.replace('js', 'ts')))) | ||
return getRealPath(res, options.preserveSymlinks) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason we're changing the pattern for when the real path is resolved? I kinda prefer the old way as the function would return the finalized resolved path directly, but maybe I'm missing an optimization made here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See 0faf06c, doing it in this way avoids creating many promises in this function.
But I don't think we will be doing it this way if we merge #15279. I'm unsure whether generating all the promises is justified. readdirSync takes almost no time compared to other things, and we could expand the tree in parallel (but keeping get/set sync). There are some fsp.readFile in this PR though. I think we need to reevaluate and maybe break this one in pieces after #15279
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the idea of this PR is fine to merge too even if there's no surface improvement. But IIRC what dominik discovered for tsconfck was that async within graph is better for large fs, sync within graph is better for small fs.
See 0faf06c, doing it in this way avoids creating many promises in this function.
I don't think we should optimize the code to avoid creating promises, it should work fine like before as long as it's scheduled 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why won't we try to avoid creating 10+ promises for each tryCleanFsResolve
🤔
Doing an await for them in a for loop isn't free either. We have merged PRs to move from a .startsWith('/')
to [0] === '/'
. I would imagine this has a way bigger effect, more thinking on the garbage collector, etc. But I think we will revert this change if we want to merge this one.
About populating the fs tree cache async, ya, we need to review what works better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I can't argue that no await is definitely faster that some awaits 😄 I don't mind as much now looking more at it, but initially I didn't think it was worth the perf. startsWith
was different that it was an easy change, otherwise I would keep startsWith
for readability.
Closing as this PR has a lot of conflicts now, we can review if we want to apply similar changes later on but this isn't seems to be something we need to do right now. |
Description
getRealPath
in resolve is taking ~8% of the workload in https://github.com/yyx990803/vite-dev-server-perf, and it is callingfs.realPathSync.native
. After this PR, it takes ~2% by usingfsp.realPath
(equivalent to the.native
sync version).I tested the speed improvement using the test in the repo instead of the cpuprofile, but it isn't stable on my machine. I think that even if we now have a ton of promises being generated, the non-blocking
fs
calls should still make this change worth it. We need to test in some other benchmarks.The PR also changes a
fs.readFileSync
inpackage.ts
to be async, and that triggered a whole bunch of async/await changes in the codebase too.What is the purpose of this pull request?