-
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
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(resolve): increase resolve speed via resolve result cache #12395
Conversation
Run & review this pull request in StackBlitz Codeflow. |
This is really interesting @sun0day! And it speaks volumes of how much we can win by optimizing the resolution of bare imports. My first reaction to the cache at this level is that it may introduce new failure points. Something we need to do, for example, is to clean the cache when a new deps optimization is committed, as this invalidates the resolved optimized URLs. I'm trying to think if something else could outdated the resolution, and it may actually end up being safe if we are careful. Another idea to avoid this caching could be to do the caching at the realpath level. See: Maybe before moving forward with this PR, we should check @bluwy's idea of replacing |
Yeah, I've thought about cache But if we can cache resolve calls at a high level(e.g On the other hand, I have to admit that this PR has some flaws like you said, but I think these flaws won't be a blocker. |
Yes, I agree. It is a compelling place for a cache. We have other caches already in place for bare imports, for example there is a cache for shouldExternalizeForSsr for an id. And it currently only depends on the resolved config. So it is expected that the resolution is kept stable by user plugins. We are also caching the resolution of optimized deps (through the optimizer), so we are already halfway there. I'm interested in seeing what others think here. I think we can review this idea and if we find it is safe, try it out during the next minor beta (we should avoid merging bigger changes now so we can release 4.2). |
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.
Thanks for investigating the resolve performances, much needed.
The issue is that with all the extension logic going on, any new file could invalidate the resolution. Maybe we could add some time to leave checks (like 5-10s) so we get the speed up on dev-server startup where probably new files aren't created but we don't get stale cache during HMR.
@@ -126,6 +126,7 @@ export function resolvePlugin(resolveOptions: InternalResolveOptions): Plugin { | |||
} = resolveOptions | |||
|
|||
const { target: ssrTarget, noExternal: ssrNoExternal } = ssrConfig ?? {} | |||
const resolvedCache = new Map() |
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 it possible to type the Map?
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 Map
should be replaced by a WeakMap<ResolvedConfig,...>
like we are doing with other caches
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 am not sure whether ResolvedConfig
's shallow value will change or not. My first reaction is using JSON.stringify(ResolvedConfig)
as a cache key, something like Map<string, string>
or WeakMap<string, string>
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.
Given the current usage I think at least we should have something like Map<string, string>()
.
Maybe WeakMap is better for memory management? I don't know the current usages
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.
We are already using the same pattern for all other caches (as the one linked in my last message). ResolvedConfig
can be considered immutable once the dev server is running or the rollup build started (the only case where it could be modified is in the configResolved
hooks in some plugins, but is more like a hack). If the ResolvedConfig
changes, all values should be invalidated so having the Cache against it works well. It is also done like this because the plugins are cloned when the server restarts or there is a rebuild, so we need this general invalidation.
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.
Sorry, I misunderstood you before. Now I get it. The resolvedCache
here is always new every time resolvePlugin
is called whenever the server restarts or there is a rebuild. You can think of it as WeakMap<ResolvedConfig, ResolvedCache>
's pair value. I am fine to use WeakMap
to store it for better memory management.
Even if a time-based cache sounds safer I think in this case we should only proceed if we can properly invalidate the cache when needed. If it is safe, then we don't need the time check. If it isn't safe, then we better avoid this optimization. Do you have a good example of a new file invalidating the resolution of bare imports? I think it would be fine as far as we respect the current limitations. For example, the dev server watcher is ignoring @sun0day one thing I see is that the |
Yeah. I also test the performance with |
Using
|
I tested both the performances in the This pr is somewhat experimental and maybe unsafe, so feel free to close it. |
Thanks for testing this again. A hunch I had is also that the |
Doubling on the thanks to you @sun0day, this PR was important in pointing us in the right direction and was one of the ideas that ended up finally pushing the current optimization sprint. Let's close it as suggested. We can revisit later if caching at a different level could help in the future. |
Description
refs #12363
I found that there are tons of duplicate bare package import requests sent to
resolve.ts
. So I guess we can optimize this via aMap
.I tested the reproduction mentioned in #6030 in my local macos, after this PR, the speed of resolving each module will increase about 7-10 times and the
realpath
's self time will decrease about 100 times.After PR:
Before PR:
Additional context
What is the purpose of this pull request?
Before submitting the PR, please make sure you do the following
fixes #123
).