-
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
Support splitting transpilation into a worker in tsc #54461
Comments
See also #54256 |
Related: #54256 |
fatcerberus by 2 seconds! |
Worth noting that unlike parallelizing of parsing, splitting off the transpilation doesn't need the asynchronicity to penetrate nearly as deeply into the compiler. The transpilation worker can be kicked off and forgotten about until the very end, when the process waits for the result before logging diagnostics. Each resolve/parse/bind/check/emit step is still internally 100% synchronous, the asynchrononicity only gets introduced at the top-level orchestration. |
Why is this dependent on |
As to why import { SomeInterface } from './foo';
export class Foo implements SomeInterface {}; Normal compilation: export class Foo {}; Transpilation: import { SomeInterface } from './foo'; // Invalid runtime code
export class Foo {}; Edit: Above is due to a precise configuration of options I'm using for transpilation. Specifically, More detailed example: import { SomeInterface, SomeBaseClass } from './foo';
export class Bar extends SomeBaseClass implements SomeInterface {}; Expected result: import { SomeBaseClass } from './foo';
export class Bar extends SomeBaseClass {}; The above works as-is with export class Bar extends SomeBaseClass {}; Note the missing import for To get that import back, we can add import { SomeInterface, SomeBaseClass } from './foo';
export class Foo extends SomeBaseClass {}; This has an invalid runtime import of The final solution is to enable import { type SomeInterface, SomeBaseClass } from './foo';
export class Bar extends SomeBaseClass implemenets SomeInterface {}; And this finally yields the expected result (while also not spending time in the type checker in the CPU profile): import { SomeBaseClass } from './foo';
export class Bar extends SomeBaseClass {}; Edit again: TL;DR, reference counting of imports happens in the type checker, not the binder, so completely disabling the type checker disables reference counting of imports, so identifying used vs. unused imports goes with it. |
FWIW
Yeah, my understanding was that that function doesnβt actually disable type checking, it just suppresses the diagnostics. |
I'm actually still on an older version and so using the My implementation in |
Suggestion
π Search Terms
isolatedModules, tsc, worker, parallel
β Viability Checklist
My suggestion meets these guidelines:
β Suggestion
For projects that have both
isolatedModules: true
andverbatimModuleSyntax: true
, it is feasible to save end-to-end compile time by, after identifying changed source files, fork the transpilation part of the compilation into a worker (WebWorker on web, 'node:worker_threads' on NodeJs) so that it can be run in parallel with the type checker on the main thread (which getsdeclarationOnly: true
injected into its config before emit).I've prototyped this in the RC version of Heft here: microsoft/rushstack#4120
However, it seems like a straightforward enough feature with enough of a performance benefit that it would be useful to have as part of core TypeScript.
π Motivating Example
With
isolatedModules: true
andverbatimModuleSyntax: true
, the time calculations are thus:T_Transpile = Transform_js + Emit_js
T_Declaration = Check + Transform_dts + Emit_dts
T_Worker_Overhead = Parse + Bind
T_Startup = Resolve + Parse + Bind
T_Worker = T_Worker_Overhead + T_Transpile
T_Original = T_Startup + T_Transpile + T_Declaration
T_WithWorker = T_Startup + max(T_Declaration, T_Worker)
Testing on a modestly large local project (816 source files) has
T_Original = 19.7 s
T_Declaration = 10.0 s
T_Worker = 8.1 s
T_Worker_Overhead = 1.9 s
Resulting in a net end-to-end savings of
6.2 s
out of19.7 s
=31%
with no change to the output.π» Use Cases
Granted, custom build tools can do this, but a lot of developers prefer to use
tsc
directly, and by making this a core feature, it incentivizes improvements to the duplication that occurs in the implmentation (namely Parse + Bind).The text was updated successfully, but these errors were encountered: