-
-
Notifications
You must be signed in to change notification settings - Fork 218
perf: rework snapshot hot paths #2852
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
base: master
Are you sure you want to change the base?
Conversation
|
**`fromNonSveltePath`** This function was originally using `replace` to unix-ify paths before then testing against them with `endsWith`. We can instead use static patterns on the unaltered path regardless of separator. **`deleteUnresolvedResolutionsFromCache`** This was originally using `split` to "parse" the cache key into a file/specifier pair. We don't actually need to do this as we know there's always one occurrence of `CACHE_KEY_SEPARATOR`. So we can instead just `slice` on the index of the separator. Additionally, we only compute any of this the first time we encounter an unresolved cache entry. I then noticed that `fileNameWithoutEnding` could be `a` (from `a.svelte`), for example. This then meant we would delete all cache entries with specifiers containing `a` (since we did `specifier.includes(fileNameWithoutEnding)`). I have changed this to explicitly test for these two cases instead: - Exact match (i.e. the specifier is exactly `fileNameWithoutEnding`) - Path-like string includes specifier (`/{fileNameWithoutEnding}.`) For example, `a.svelte` would become `a`. We then want to check for `/a.` to be able to match `/a.svelte`, `/a.svelte.js`, etc. Nobody can import it without a `/` preceding it since then it'd be a bare specifier. **`getKey`** We currently key by file name and specifier. It shouldn't be possible for two sibling files to resolve the same specifier to two different modules. So it seems like we should be able to key by `dirname(path)` instead. This means we would cache resolution for entire directories rather than re-computing it for every file in that directory. **`getLastPartOfPath`** This now uses `slice` on the last separator rather than splitting only to retrieve the last part.
862453c
to
2cfcfbd
Compare
It's possible when one file is a For the possible bug with "fileNameWithoutEnding", it's more of an "overly aggressive" cache invalidation. We might have to think more about if there are other cases where the new check doesn't cover. |
hmm, you are right. there must be something we can do here though. the current keying means a large project repeatedly resolves the same module many times. this usually involves file system calls too, which is what takes so long (since ts' own resolveModule hits the file system for path resolution). i didn't encounter a directory cache, can you point me at the code you're talking about?
extremely aggressive cache invalidation 😅 quite a lot of files will overmatch. what if i have short filenames like |
The
|
do we?
given the current logic before this PR: language-tools/packages/language-server/src/plugins/typescript/module-loader.ts Lines 69 to 73 in dec37ea
a key might be if we're deleting caches for for example, if we had this cache:
we will clear all the
we don't clear the per directory cache edit: ok cool you realised this 😂 |
FYI im not encountering any issue, and don't have any obvious reproduction i decided to profile if you want more info you're probably better off chatting to me on discord about it or something as this is the result of reading through a whole bunch of CPU profiles using shadcn-svelte as a test subject |
You're saying it improves the performance of svelte-check and not the watch mode, right? Then I think it's better to make it not call |
yes i'm solely profiling if we can avoid calling it altogether, great 😄 i can revert the key change for now. can you explain a bit more where you think we can avoid calling the deletion function, and why? |
Re key: Could we only use the filename if we detect the file name ending is |
Reverts the `getKey` change and updates diagnostics iteration to avoid allocating an array we later discard.
FYI i have discarded the key change for now and got rid of a bit of garbage collection around diagnostic iteration we could possibly merge this one as-is when we're happy and work on the key stuff in a follow up i would like to figure it out since it seems we invalidate caches far too often right now doing something with extensions makes sense to me Edit: I'll fix the tests when I'm next at a laptop 👍 |
fromNonSveltePath
This function was originally using
replace
to unix-ify paths beforethen testing against them with
endsWith
.We can instead use static patterns on the unaltered path regardless of
separator.
deleteUnresolvedResolutionsFromCache
This was originally using
split
to "parse" the cache key into afile/specifier pair.
We don't actually need to do this as we know there's always one
occurrence of
CACHE_KEY_SEPARATOR
. So we can instead justslice
onthe index of the separator.
Additionally, we only compute any of this the first time we encounter an
unresolved cache entry.
Possible bug
I then noticed that
fileNameWithoutEnding
could bea
(froma.svelte
), for example. This then meant we would delete all cacheentries with specifiers containing
a
(since we didspecifier.includes(fileNameWithoutEnding)
).I have changed this to explicitly test for these two cases instead:
fileNameWithoutEnding
)/{fileNameWithoutEnding}.
)For example,
a.svelte
would becomea
. We then want to check for/a.
to be able to match/a.svelte
,/a.svelte.js
, etc. Nobody canimport it without a
/
preceding it since then it'd be a barespecifier.
getLastPartOfPath
This now uses
slice
on the last separator rather than splitting onlyto retrieve the last part.
diagnostics
In the root svelte-check logic, we were previously allocating an array of the diagnostics from various sources then later discarding that array (garbage collecting it).
I have changed this to instead iterate through each diagnostics set one by one, so we never allocate a new array.
Why, James?
Locally this reduces
svelte-check
in shadcn-svelte's own docs from ~14s to ~10s.Most of this is the change in keying strategy (
getKey
), but the lazy name computation helped a lot too.