-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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: Node.getChildren
speed regression in ts 5.5 when used at very large scale
#59101
Comments
I didn't get that far on this today, but I believe this is from more time spent in the GC. If you try upping your As an aside, SO much time in your build is being spent on shims running within eslint-plugin-react. While |
This is the opposite of expected! Which walk function is that? I'm not familiar with that "walk" function. Traces would be helpful, but walkerdb/sentry#1 is probably enough to give us a lead. You may also consider running things via https://www.npmjs.com/package/pprof-it (what Daniel just used above) as it in my experience does a better job attributing time and memory allocations to specific lines of code. |
Hm, my trace doesn't seem to show that particular code as to blame: @walkerdb What tool did you use to find your result? |
I timed this, and in 5.5, it's 100s, but 5.4 is 91s; not 2-3x slower from what I can tell... |
A second run of 5.4 was 100s too. Not sure what's going on here. What version of Node are you using? |
I was using Node 22; switching down to Node 18 shows a worse regression:
Not 2-3x but very much farther apart than Node 22. Unfortunately I'm now going to be gone until next week, so this will just have to eat me up inside until then... |
Hi @jakebailey! Sorry, I should have clarified a few things:
to @DanielRosenwasser I'm not a part of sentry, I was just using that repo as an example repro since ours is private. |
the types in Notion's private monorepo can be quite complex, and I suspect are close to a pathological case for some typescript perf behavior, which may help explain the 2.5x slowdown we were observing. I'll see if I can hack running the same checks under node 22 to see if the timings change there much. |
Ran Node 18, and the results are very different from #59101 (comment): That is an incredible amount of GC time, along with the badness Daniel noted above. Given the difference between Node 18 and Node 22 on this being in that shim code, I'm not 100% certain that this repro is representative of the repo you're testing internally, unfortunately... Would you be able to run |
@jakebailey I'll give pprof-it a try, ty! |
here's my pprof result for typescript 5.5.2 on node 18.18. I'll also upload one for typescript 5.4 in a sec |
pprof results for the exact same run but using typescript 5.4.5 on node 18.18 . This is significantly faster, and matches our expected run time from prior to the 5.5 update |
last run, with typescript 5.5.2 and node 22.3.0. Running under a newer node version doesn't seem to actually be much faster in this case? |
In all of those profiles, nearly half the time is spent just getting/setting the node children via the WeakMap. Yikes. I'm not sure why this isn't observable outside of your internal code unless WeakMaps get noticably slower when they get massive. May have to tack that prop back on again, but the prop not being on all nodes directly was one of the things we had to do to not blow up perf too much. |
At least I assume it's the weak map itself; haven't quite figured out how to remap the paths in the profile to a real version. (I'm also on a plane, that doesn't help) |
thanks so much @jakebailey and @DanielRosenwasser for digging in! Happy to help however I can, feel free to shoot me a message at walker@makenotion.com if there's any other info you could use / commands to run / code to share / etc that I can help with. In the meantime though we can wait through the holiday week, hope you have a great vacation! |
can confirm that the TypeScript/src/services/services.ts Line 445 in ddf43cd
here also are some pprof diff screenshots between the 5.4.5 run and the 5.5.2 run, generated with |
@walkerdb any chance you can iterate and reduce this repro to see if there's a root cause beyond just scale? For example, if you cut out half of the files in your program - even if there are build errors, do you see the same surprising amount of time in |
This is probably just scale and repeated reallocation of the WeakMap at larger and larger sizes. It might be worth providing a facility for these APIs to be handed a scoped cache object (or using the SourceFileLike ancestor as the cache object, where applicable), rather than using a single global WeakMap. |
Node.getChildren
speed regression in ts 5.5 when used at very large scale
@DanielRosenwasser I just gave a code bisect a go to see if a specific module or type is causing problems. As far as I can tell it's unrelated to the specific code being parsed, but as @dmichon-msft just mentioned it seems related to scale -- past a certain line-of-code count Specifically, I'm noticing the Running the same checks on smaller subsets of the same set of files results in expected fast performance. Also for additional context, normal typecheck time with |
Even this much information is great though. I think my team now has a clear path forward for us to make the 5.5 bump, just split our typed lint job into smaller subsets under the LOC perf limit and overall runtime shouldn't be a long pole in our CI. |
So I created #59154 to experiment a bit with @dmichon-msft's suggestion. If you want to take a profile with it, you can install npm install -D @typescript-deploys/pr-build@5.6.0-pr-59154-2 Note that this is an experiment and it's pretty risky perf-wise for the language service. This change means that populating the list of child nodes now needs a walk up to the source file itself. So no guarantees that we land it, but it could inform other work. |
It could also be slower for you too in a different way. It means that simply requesting the children of the tree grows to something like |
@DanielRosenwasser looks like that change does resolve nearly all the perf difference for us between ts 5.4 and 5.5! Running our typed-lint suite with pprof-time-typescript-childrenCachedBySourceFile.pb.gz |
@walkerdb that's really great! I think we will discuss this internally and try to figure out if we ship this specific change, and if we can back-port it. One thing that would help us a lot is if you could try that build as your daily driver. In VS Code, you can configure a workspace version of TypeScript by following the docs here or just setting the {
"typescript.tsdk": "./node_modules/typescript/lib"
} |
Can do, thank you for all your help! Is there any specific feedback that would help on the new version other than whether tsserver requests like getProgram, getSemanticDiagnostics, getCompletionsAtPosition etc still seem to work and are just as responsive as before? (we track perf metrics for each of them via a custom plugin so it should be pretty easy for us to tell whether there's been any major regression there) |
This sounds amazing, and I absolutely want to hear more about this. This is the first we've heard of someone tracking this to this extent so anything is awesome. |
Had a huge https://discord.com/channels/1026804805894672454/1254556633556713553/1255160613441769683 Setting my typescript dependency to |
Hey all, this is fixed in the If you are actually able to measure, we are specifically looking for differences in memory usage, changes in editor operation delays, as well as changes in variance in delays. If things feel good, we can cherry-pick the change back into 5.5. |
@DanielRosenwasser can do! I'll get a test group going internally tomorrow, will see if metrics move much. We do also track total tsserver memory usage, should be able to see if that moves much under the new version as well. @jakebailey happy to chat more about our tsserver observability setup! Could talk sync / show a few dashboards if you're interested? |
and thank you both again for moving so fast on this, much appreciated! |
+1 from the typescript-eslint side, we really appreciate it! Thanks so much for the fast follow and (very interesting) deep dive everyone! |
following up to say we haven't noticed any serious perf regressions in IDE usage so far. We haven't had the participation numbers to say much more than that though. |
Likewise on stability in my testing in very large monorepo (with a very large monolith). Curious if there are plans on a 5.5 backport? Or still targeting 5.6? |
The backport is open here: #59211 |
TypeScript 5.5.4 should contain the fix - thanks for reporting everyone! |
TypeScript 5.5.4 apparently speeds up linting by a lot: microsoft/TypeScript#59101
🔎 Search Terms
typescript 5.5 performance, slowdown, typescript-eslint
🕗 Version & Regression Information
git bisect
on a local typescript repo, it slows down specifically on this commit)⏯ Playground Link
No response
💻 Code
I've put together a repro in a forked version of the sentry monorepo here: walkerdb/sentry#1. It's using the sentry repo only because it's a large public repo that uses typescript-eslint.
🙁 Actual behavior
When running typescript-eslint's type-aware lint rules in a large monorepo with typescript 5.5, we observe lint times between 1.3x to 3x worse than when running the exact same rules and the same config under typescript 5.4. The slowdown seems to be entirely coming from the monomorphized objects change in the ts 5.5 release.
🙂 Expected behavior
There should be no performance impact when moving from typescript 5.4 to 5.5.
Additional information about the issue
We've submitted a bug report to typescript-eslint but we're also reporting here since the slowdown can be pinpointed to a specific typescript change.
Also attaching some before/after update perf traces via 0x that show the extra time is coming largely from typescript internals. The highlighted boxes show the specific part of the process that has ballooned in runtime. I'm happy to share the actual traces with maintainers if they'd be useful in debugging.
The text was updated successfully, but these errors were encountered: