Skip to content

perf: reduce GC pressure by hoisting script target features object #55484

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

Conversation

Havunen
Copy link
Contributor

@Havunen Havunen commented Aug 23, 2023

This PR reduces GC pressure by hoisting ScriptTargetFeatures instead of creating that object over and over again.

image

@typescript-bot typescript-bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Aug 23, 2023
@typescript-bot
Copy link
Collaborator

This PR doesn't have any linked issues. Please open an issue that references this PR. From there we can discuss and prioritise.

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above; also, general note, but we typically only uppercase things that are types.

@DanielRosenwasser
Copy link
Member

@typescript-bot perf test this

@typescript-bot
Copy link
Collaborator

typescript-bot commented Aug 23, 2023

Heya @DanielRosenwasser, I've started to run the regular perf test suite on this PR at f849917. You can monitor the build here.

Update: The results are in!

@jakebailey
Copy link
Member

What are you testing where you're seeing this be costly? Is this likely to be shown in tsc or tsserver benchmarks?

@Havunen
Copy link
Contributor Author

Havunen commented Aug 23, 2023

What are you testing where you're seeing this be costly? Is this likely to be shown in tsc or tsserver benchmarks?

I'm trying to make our application build faster. Its webpack5 vue2 typescript app

@DanielRosenwasser
Copy link
Member

For a single compilation it's surprising that we even end up doing this more than once. This is only supposed to occur as part of error recovery for an unresolved type. Do you have a lot of unchecked JavaScript code or something?

@Havunen
Copy link
Contributor Author

Havunen commented Aug 23, 2023

For a single compilation it's surprising that we even end up doing this more than once. This is only supposed to occur as part of error recovery for an unresolved type. Do you have a lot of unchecked JavaScript code or something?

Here is the call stack:

image

@typescript-bot
Copy link
Collaborator

@DanielRosenwasser
The results of the perf run you requested are in!

Here they are:

Compiler

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Angular - node (v16.17.1, x64)
Memory used 300,343k (± 0.01%) 300,269k (± 0.00%) -74k (- 0.02%) 300,261k 300,277k p=0.005 n=6
Parse Time 3.02s (± 0.40%) 3.03s (± 0.17%) ~ 3.03s 3.04s p=0.114 n=6
Bind Time 0.93s (± 0.00%) 0.93s (± 0.00%) ~ 0.93s 0.93s p=1.000 n=6
Check Time 9.49s (± 0.29%) 9.30s (± 0.30%) -0.19s (- 2.04%) 9.26s 9.33s p=0.005 n=6
Emit Time 7.63s (± 0.23%) 7.62s (± 0.23%) ~ 7.60s 7.65s p=0.462 n=6
Total Time 21.08s (± 0.14%) 20.89s (± 0.13%) -0.19s (- 0.91%) 20.84s 20.92s p=0.005 n=6
Compiler-Unions - node (v16.17.1, x64)
Memory used 193,894k (± 0.01%) 193,905k (± 0.00%) ~ 193,895k 193,917k p=0.423 n=6
Parse Time 1.57s (± 1.25%) 1.55s (± 0.53%) ~ 1.55s 1.57s p=0.059 n=6
Bind Time 0.79s (± 0.00%) 0.79s (± 0.52%) ~ 0.79s 0.80s p=0.405 n=6
Check Time 9.93s (± 0.25%) 9.91s (± 0.13%) ~ 9.89s 9.92s p=0.190 n=6
Emit Time 2.74s (± 0.19%) 2.74s (± 0.27%) ~ 2.73s 2.75s p=0.241 n=6
Total Time 15.04s (± 0.28%) 15.00s (± 0.11%) ~ 14.98s 15.02s p=0.142 n=6
Monaco - node (v16.17.1, x64)
Memory used 347,148k (± 0.00%) 347,154k (± 0.00%) ~ 347,146k 347,167k p=0.575 n=6
Parse Time 2.69s (± 0.20%) 2.69s (± 0.19%) ~ 2.68s 2.69s p=0.640 n=6
Bind Time 0.99s (± 0.00%) 0.99s (± 0.00%) ~ 0.99s 0.99s p=1.000 n=6
Check Time 7.94s (± 0.39%) 7.95s (± 0.17%) ~ 7.93s 7.96s p=0.744 n=6
Emit Time 4.25s (± 0.18%) 4.25s (± 0.27%) ~ 4.24s 4.27s p=0.933 n=6
Total Time 15.87s (± 0.22%) 15.87s (± 0.18%) ~ 15.83s 15.91s p=0.936 n=6
TFS - node (v16.17.1, x64)
Memory used 301,138k (± 0.00%) 301,151k (± 0.01%) ~ 301,114k 301,170k p=0.128 n=6
Parse Time 2.19s (± 0.34%) 2.18s (± 0.79%) ~ 2.16s 2.21s p=0.319 n=6
Bind Time 1.11s (± 0.37%) 1.11s (± 0.00%) ~ 1.11s 1.11s p=0.405 n=6
Check Time 7.25s (± 0.36%) 7.22s (± 0.10%) -0.03s (- 0.44%) 7.21s 7.23s p=0.021 n=6
Emit Time 3.98s (± 0.26%) 3.97s (± 0.26%) ~ 3.95s 3.98s p=0.241 n=6
Total Time 14.53s (± 0.24%) 14.48s (± 0.14%) -0.05s (- 0.32%) 14.44s 14.50s p=0.035 n=6
material-ui - node (v16.17.1, x64)
Memory used 479,407k (± 0.00%) 479,432k (± 0.00%) +25k (+ 0.01%) 479,421k 479,438k p=0.013 n=6
Parse Time 3.15s (± 0.17%) 3.15s (± 0.33%) ~ 3.14s 3.17s p=0.138 n=6
Bind Time 0.91s (± 0.45%) 0.91s (± 0.00%) ~ 0.91s 0.91s p=0.405 n=6
Check Time 17.78s (± 0.25%) 17.80s (± 0.28%) ~ 17.73s 17.86s p=0.376 n=6
Emit Time 0.00s (± 0.00%) 0.00s (± 0.00%) ~ 0.00s 0.00s p=1.000 n=6
Total Time 21.83s (± 0.21%) 21.87s (± 0.20%) ~ 21.80s 21.92s p=0.296 n=6
xstate - node (v16.17.1, x64)
Memory used 542,904k (± 0.02%) 542,922k (± 0.01%) ~ 542,840k 542,979k p=0.471 n=6
Parse Time 3.70s (± 0.20%) 3.70s (± 0.17%) ~ 3.69s 3.71s p=0.718 n=6
Bind Time 1.37s (± 4.64%) 1.38s (± 4.78%) ~ 1.33s 1.47s p=0.560 n=6
Check Time 3.29s (± 2.53%) 3.35s (± 0.79%) ~ 3.31s 3.39s p=0.142 n=6
Emit Time 0.08s (± 0.00%) 0.08s (± 0.00%) ~ 0.08s 0.08s p=1.000 n=6
Total Time 8.45s (± 0.39%) 8.52s (± 0.76%) +0.08s (+ 0.93%) 8.48s 8.64s p=0.034 n=6
System info unknown
Hosts
  • node (v16.17.1, x64)
Scenarios
  • Angular - node (v16.17.1, x64)
  • Compiler-Unions - node (v16.17.1, x64)
  • Monaco - node (v16.17.1, x64)
  • TFS - node (v16.17.1, x64)
  • material-ui - node (v16.17.1, x64)
  • xstate - node (v16.17.1, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

tsserver

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
Compiler-UnionsTSServer - node (v16.17.1, x64)
Req 1 - updateOpen 2,491ms (± 0.14%) 2,495ms (± 0.19%) ~ 2,487ms 2,501ms p=0.108 n=6
Req 2 - geterr 5,956ms (± 0.44%) 5,942ms (± 0.21%) ~ 5,928ms 5,965ms p=0.173 n=6
Req 3 - references 345ms (± 1.34%) 342ms (± 0.24%) ~ 341ms 343ms p=0.412 n=6
Req 4 - navto 278ms (± 0.91%) 276ms (± 0.19%) ~ 276ms 277ms p=0.070 n=6
Req 5 - completionInfo count 1,356 (± 0.00%) 1,356 (± 0.00%) ~ 1,356 1,356 p=1.000 n=6
Req 5 - completionInfo 81ms (± 8.03%) 85ms (± 6.23%) ~ 81ms 94ms p=0.167 n=6
CompilerTSServer - node (v16.17.1, x64)
Req 1 - updateOpen 2,623ms (± 0.49%) 2,625ms (± 0.65%) ~ 2,598ms 2,643ms p=0.936 n=6
Req 2 - geterr 4,767ms (± 0.24%) 4,771ms (± 0.37%) ~ 4,744ms 4,797ms p=0.872 n=6
Req 3 - references 350ms (± 0.23%) 350ms (± 0.26%) ~ 349ms 351ms p=0.550 n=6
Req 4 - navto 270ms (± 0.30%) 270ms (± 0.38%) ~ 268ms 271ms p=0.932 n=6
Req 5 - completionInfo count 1,518 (± 0.00%) 1,518 (± 0.00%) ~ 1,518 1,518 p=1.000 n=6
Req 5 - completionInfo 79ms (± 0.52%) 79ms (± 0.52%) ~ 78ms 79ms p=0.218 n=6
xstateTSServer - node (v16.17.1, x64)
Req 1 - updateOpen 2,716ms (± 0.06%) 2,722ms (± 0.39%) ~ 2,712ms 2,742ms p=0.419 n=6
Req 2 - geterr 1,949ms (± 1.98%) 1,915ms (± 2.51%) ~ 1,861ms 1,970ms p=0.199 n=6
Req 3 - references 134ms (± 7.54%) 134ms (± 5.46%) ~ 122ms 144ms p=0.685 n=6
Req 4 - navto 353ms (± 0.49%) 354ms (± 0.34%) ~ 352ms 355ms p=0.162 n=6
Req 5 - completionInfo count 2,071 (± 0.00%) 2,071 (± 0.00%) ~ 2,071 2,071 p=1.000 n=6
Req 5 - completionInfo 317ms (± 1.82%) 314ms (± 1.78%) ~ 309ms 321ms p=0.126 n=6
System info unknown
Hosts
  • node (v16.17.1, x64)
Scenarios
  • CompilerTSServer - node (v16.17.1, x64)
  • Compiler-UnionsTSServer - node (v16.17.1, x64)
  • xstateTSServer - node (v16.17.1, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Startup

Comparison Report - baseline..pr
Metric baseline pr Delta Best Worst p-value
tsc-startup - node (v16.17.1, x64)
Execution time 155.61ms (± 0.18%) 155.88ms (± 0.17%) +0.27ms (+ 0.17%) 154.78ms 160.72ms p=0.000 n=600
tsserver-startup - node (v16.17.1, x64)
Execution time 230.84ms (± 0.18%) 230.95ms (± 0.12%) +0.11ms (+ 0.05%) 229.97ms 235.91ms p=0.000 n=600
tsserverlibrary-startup - node (v16.17.1, x64)
Execution time 234.58ms (± 0.14%) 236.15ms (± 0.13%) +1.57ms (+ 0.67%) 235.00ms 242.85ms p=0.000 n=600
typescript-startup - node (v16.17.1, x64)
Execution time 235.82ms (± 0.12%) 235.64ms (± 0.12%) -0.18ms (- 0.08%) 234.49ms 239.46ms p=0.000 n=600
System info unknown
Hosts
  • node (v16.17.1, x64)
Scenarios
  • tsc-startup - node (v16.17.1, x64)
  • tsserver-startup - node (v16.17.1, x64)
  • tsserverlibrary-startup - node (v16.17.1, x64)
  • typescript-startup - node (v16.17.1, x64)
Benchmark Name Iterations
Current pr 6
Baseline baseline 6

Developer Information:

Download Benchmarks

@jakebailey
Copy link
Member

I'm certainly confused as to why this is appearing in your call stack; it's trying to produce a nice message for IArguments not existing which could only happen because your code is not typechecking. IArguments is in lib.es5.d.ts so that's extra strange and implies there's something not quite right in your build.

@jakebailey
Copy link
Member

I'm sure you've already seen, but you'll want to npm run format to get that check passing. That'll probably make the diff better too.

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR seems fine now, but I do very much question why this is helping you at all (per above).

@Havunen
Copy link
Contributor Author

Havunen commented Aug 23, 2023

It seems to have something to do with fork-ts-checker-webpack-plugin (v7.3.0) when I remove that plugin from the webpack pipeline this method does not get called as often anymore.

@jakebailey
Copy link
Member

Is it checking with the wrong tsconfig/compilerOptions or something? Missing IArguments is extremely suspect.

@jakebailey
Copy link
Member

You know, I can see how this may be a thing; this is coming out of transpileModule, so in that fork plugin, it's the side of the plugin that's doing emit, not checking. But if it's passing in the "wrong" options (or, if transpileModule doesn't give the "right" options under the assumption that it doesn't matter), but some aspect of the checker is still running, then we may get a bunch of errors that will be thrown away.

@jakebailey
Copy link
Member

Yeah, transpileModule implies noLib, so it seems to me like these errors are inevitable?

Of course, this makes me wonder if we could do a lot better about not requesting / doing work for diagnostics during transpileModule, which is roughly #50699 (specifically note #50699 (comment)).

@andrewbranch for interest 😄

In any case, this seems like a totally good optimization regardless; I can see this being checked repeatedly in the editor, for example.

@RyanCavanaugh
Copy link
Member

We could probably get a net perf win in transpileModule by providing a minimal shim lib just to reduce the number of errors reported in nontrivial files

@@ -1281,8 +1282,8 @@ export function getInternalEmitFlags(node: Node): InternalEmitFlags {
export type ScriptTargetFeatures = ReadonlyMap<string, ReadonlyMap<string, string[]>>;

/** @internal */
export function getScriptTargetFeatures(): ScriptTargetFeatures {
return new Map(Object.entries({
export const getScriptTargetFeatures: () => ScriptTargetFeatures = /* @__PURE__ */ memoize(() =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we use this annotation anywhere else - any reason you felt we needed it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had suggested sticking it on the closure inside of the memoize call but they put it here instead (which is roughly equivalent, I guess). Mainly it's that this type is an aliased ReadonlyMap. Not sure why it's named, per se

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tacked a small change onto the PR to move the annotation to the other place; wanting to merge this!

@jakebailey jakebailey merged commit 768f592 into microsoft:main Aug 23, 2023
@Havunen Havunen deleted the perf-getScriptTargetFeatures-causes-GC-pressure branch August 24, 2023 04:17
snovader pushed a commit to EG-A-S/TypeScript that referenced this pull request Sep 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Uncommitted Bug PR for untriaged, rejected, closed or missing bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants