Skip to content

Cache mapper instantiations - #61505

Merged
Jake Bailey (jakebailey) merged 13 commits into
microsoft:mainfrom
Andarist:cache-mapper-instaniations
Jun 6, 2025
Merged

Cache mapper instantiations#61505
Jake Bailey (jakebailey) merged 13 commits into
microsoft:mainfrom
Andarist:cache-mapper-instaniations

Conversation

@Andarist

@AndaristMateusz Burzyński (Andarist) commented Mar 30, 2025

Copy link
Copy Markdown
Contributor

David Blass (@ssalbdivad) found out that a chain of somewhat trivial operations can easily end up with dreaded "Type instantiation is excessively deep and possibly infinite".

Two different reproductions were created:

  1. standalone signature receiving its previous output as input in a chain: TS playground
  2. a dotted chain where a method returns a new instance of its own enclosing type alias: TS playground

We were able to bisect both to two different past PRs - one of which is quite old:

  1. the first one bisects to this PR and we can see it performing better in TS 5.4 here
  2. the other one bisects to this PR and we can see it performing better in TS 4.1 here

⚠️ that said - the "chain setup" avoids the error better but the property type lookup can still lead to the error. So, in some sense, the error just shifted place. this has been solved

I dug into this and what I found out is, those types refer to the same "base" types - they are chained from them and instantiateType repeats the work on them a lot. At least part of the problem is that getObjectTypeInstantiation calls instantiateTypes(type.aliasTypeArguments, mapper) to create part of its cache key. So it can't even check if the result is cached before it instantiates those. This problem compounds heavily in longer chains like this.

So I toyed with possible solutions and I realized that caching results on mappers could help here... to some extent at least. I don't think it's the best solution but I also don't yet understand this problem to its core to propose anything better.

It looks like this has very positive impact on instantiation counts across the test suite. It surely trades some memory for it though. It would be interesting to see perf results for this and the extended test suite run (even if only to get new data points for further investigation).

EDIT:// David Blass (@ssalbdivad) has tested this in some trpc-based repositories and the check time got halved for them. The current version is also able to typecheck the 50-item deep "chains" as it can be seen in the added long* tests. Those previously were only instantiable to a certain depth (smth between 15-25, depending on the test case) and the last items in the chain were being instantiated really slow. Now those longer ones are handled pretty fast.

@github-project-automationgithub-project-automationBot moved this to Not started in PR BacklogMar 30, 2025
@typescript-botTypeScript Bot (typescript-bot) added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Mar 30, 2025
@typescript-bot

Copy link
Copy Markdown
Contributor

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

Comment threadsrc/compiler/checker.ts Outdated
Comment threadsrc/compiler/checker.ts Outdated
@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it

@typescript-bot

TypeScript Bot (typescript-bot) commented Mar 30, 2025

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
test top400✅ Started
user test this✅ Started
run dt✅ Started
perf test this faster✅ Started

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it

@typescript-bot

TypeScript Bot (typescript-bot) commented Mar 31, 2025

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
test top400✅ Started✅ Results
user test this✅ Started👀 Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the user tests with tsc comparing main and refs/pull/61505/merge:

Something interesting changed - please have a look.

Details

effect

packages/effect/benchmark/tsconfig.json

tsconfig.json

tsconfig.build.json

tsconfig.base.json

packages/typeclass/dtslint/tsconfig.json

packages/platform/dtslint/tsconfig.json

packages/effect/dtslint/tsconfig.json

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors3434~~~p=1.000 n=6
Symbols62,39062,390~~~p=1.000 n=6
Types50,39550,395~~~p=1.000 n=6
Memory used194,709k (± 1.01%)196,843k (± 0.98%)~194,935k198,710kp=0.173 n=6
Parse Time1.31s (± 0.31%)1.31s (± 0.57%)~1.30s1.32sp=1.000 n=6
Bind Time0.73s0.73s~~~p=1.000 n=6
Check Time9.73s (± 0.49%)9.77s (± 0.35%)~9.71s9.81sp=0.228 n=6
Emit Time2.73s (± 0.54%)2.73s (± 0.65%)~2.71s2.75sp=0.935 n=6
Total Time14.51s (± 0.37%)14.54s (± 0.16%)~14.50s14.56sp=0.253 n=6
angular-1 - node (v18.15.0, x64)
Errors5656~~~p=1.000 n=6
Symbols948,670948,670~~~p=1.000 n=6
Types410,947410,947~~~p=1.000 n=6
Memory used1,224,296k (± 0.00%)1,248,572k (± 0.00%)+24,276k (+ 1.98%)1,248,514k1,248,618kp=0.005 n=6
Parse Time6.63s (± 1.21%)6.63s (± 0.67%)~6.56s6.70sp=1.000 n=6
Bind Time1.88s (± 0.67%)1.88s (± 0.73%)~1.87s1.90sp=0.557 n=6
Check Time31.93s (± 0.37%)32.31s (± 0.31%)+0.39s (+ 1.21%)32.18s32.47sp=0.005 n=6
Emit Time15.23s (± 0.22%)15.26s (± 0.24%)~15.21s15.32sp=0.147 n=6
Total Time55.67s (± 0.25%)56.09s (± 0.21%)+0.42s (+ 0.75%)55.98s56.31sp=0.005 n=6
mui-docs - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols2,461,2672,461,267~~~p=1.000 n=6
Types893,340893,340~~~p=1.000 n=6
Memory used2,310,804k (± 0.00%)2,342,249k (± 0.00%)+31,445k (+ 1.36%)2,342,222k2,342,302kp=0.005 n=6
Parse Time9.04s (± 0.22%)9.04s (± 0.17%)~9.02s9.06sp=1.000 n=6
Bind Time2.27s (± 0.36%)2.26s (± 0.53%)~2.25s2.28sp=0.652 n=6
Check Time75.67s (± 0.38%)75.95s (± 0.42%)~75.43s76.42sp=0.173 n=6
Emit Time0.29s (± 1.92%)0.29s (± 1.80%)~0.28s0.29sp=0.640 n=6
Total Time87.26s (± 0.34%)87.55s (± 0.35%)~87.03s87.99sp=0.173 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,226,7131,226,807+94 (+ 0.01%)~~p=0.001 n=6
Types266,991267,000+9 (+ 0.00%)~~p=0.001 n=6
Memory used2,357,616k (± 0.03%)2,376,351k (± 0.02%)+18,735k (+ 0.79%)2,375,702k2,377,087kp=0.005 n=6
Parse Time5.20s (± 1.17%)5.17s (± 0.50%)~5.14s5.21sp=0.297 n=6
Bind Time1.79s (± 1.91%)1.78s (± 0.68%)~1.77s1.80sp=0.744 n=6
Check Time35.40s (± 0.56%)35.58s (± 0.33%)~35.41s35.75sp=0.066 n=6
Emit Time2.99s (± 2.22%)2.99s (± 1.20%)~2.96s3.06sp=0.630 n=6
Total Time45.40s (± 0.72%)45.53s (± 0.17%)~45.45s45.62sp=0.066 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,226,7131,226,807+94 (+ 0.01%)~~p=0.001 n=6
Types266,991267,000+9 (+ 0.00%)~~p=0.001 n=6
Memory used2,911,961k (±12.87%)3,067,098k (± 9.90%)🔻+155,137k (+ 5.33%)2,446,535k3,191,944kp=0.031 n=6
Parse Time6.94s (± 1.45%)6.94s (± 1.80%)~6.70s7.04sp=1.000 n=6
Bind Time2.14s (± 2.28%)2.12s (± 1.06%)~2.08s2.14sp=0.810 n=6
Check Time42.65s (± 0.57%)42.85s (± 0.37%)~42.57s43.01sp=0.128 n=6
Emit Time3.45s (± 2.05%)3.53s (± 0.82%)+0.07s (+ 2.17%)3.49s3.58sp=0.044 n=6
Total Time55.20s (± 0.59%)55.45s (± 0.48%)~54.92s55.69sp=0.128 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols262,674262,691+17 (+ 0.01%)~~p=0.001 n=6
Types106,849106,858+9 (+ 0.01%)~~p=0.001 n=6
Memory used440,486k (± 0.01%)449,610k (± 0.02%)+9,124k (+ 2.07%)449,509k449,723kp=0.005 n=6
Parse Time3.54s (± 0.42%)3.51s (± 0.91%)~3.48s3.57sp=0.052 n=6
Bind Time1.31s (± 0.39%)1.32s (± 0.80%)~1.30s1.33sp=0.794 n=6
Check Time19.05s (± 0.31%)19.04s (± 0.36%)~18.93s19.13sp=0.810 n=6
Emit Time1.52s (± 0.65%)1.53s (± 0.79%)~1.51s1.54sp=0.246 n=6
Total Time25.43s (± 0.27%)25.39s (± 0.32%)~25.29s25.53sp=0.423 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors7171~~~p=1.000 n=6
Symbols225,981225,981~~~p=1.000 n=6
Types94,35694,356~~~p=1.000 n=6
Memory used371,214k (± 0.01%)379,734k (± 0.01%)+8,519k (+ 2.29%)379,686k379,811kp=0.005 n=6
Parse Time2.89s (± 0.60%)2.91s (± 0.96%)~2.86s2.93sp=0.192 n=6
Bind Time1.61s (± 1.69%)1.60s (± 1.46%)~1.57s1.63sp=0.333 n=6
Check Time16.51s (± 0.33%)16.54s (± 0.26%)~16.49s16.61sp=0.574 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time21.02s (± 0.26%)21.04s (± 0.25%)~20.99s21.12sp=0.628 n=6
vscode - node (v18.15.0, x64)
Errors33~~~p=1.000 n=6
Symbols3,336,0593,336,059~~~p=1.000 n=6
Types1,130,5071,130,507~~~p=1.000 n=6
Memory used3,394,122k (± 0.01%)3,466,746k (± 0.01%)+72,624k (+ 2.14%)3,466,227k3,466,964kp=0.005 n=6
Parse Time14.70s (± 0.34%)14.60s (± 0.53%)-0.10s (- 0.71%)14.51s14.69sp=0.030 n=6
Bind Time4.67s (± 0.62%)4.69s (± 0.61%)~4.66s4.74sp=0.514 n=6
Check Time93.33s (± 2.44%)93.32s (± 3.35%)~91.13s98.75sp=0.936 n=6
Emit Time29.88s (± 2.66%)30.10s (± 2.92%)~29.44s31.36sp=0.575 n=6
Total Time142.58s (± 2.06%)142.71s (± 2.81%)~139.84s149.43sp=1.000 n=6
webpack - node (v18.15.0, x64)
Errors22~~~p=1.000 n=6
Symbols310,441310,441~~~p=1.000 n=6
Types136,030136,030~~~p=1.000 n=6
Memory used465,164k (± 0.02%)475,453k (± 0.01%)+10,290k (+ 2.21%)475,362k475,517kp=0.005 n=6
Parse Time5.15s (± 0.56%)5.16s (± 0.94%)~5.13s5.26sp=0.418 n=6
Bind Time2.26s (± 0.99%)2.26s (± 1.23%)~2.24s2.31sp=0.934 n=6
Check Time25.48s (± 0.42%)25.67s (± 0.18%)+0.19s (+ 0.73%)25.62s25.74sp=0.005 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time32.89s (± 0.34%)33.09s (± 0.27%)+0.21s (+ 0.63%)33.00s33.25sp=0.013 n=6
xstate-main - node (v18.15.0, x64)
Errors55~~~p=1.000 n=6
Symbols567,041567,041~~~p=1.000 n=6
Types189,889189,889~~~p=1.000 n=6
Memory used497,780k (± 0.02%)519,637k (± 0.02%)🔻+21,857k (+ 4.39%)519,555k519,800kp=0.005 n=6
Parse Time3.36s (± 0.81%)3.37s (± 0.62%)~3.34s3.40sp=0.570 n=6
Bind Time1.21s (± 1.10%)1.20s (± 1.14%)~1.19s1.23sp=0.323 n=6
Check Time19.99s (± 0.10%)19.81s (± 0.38%)-0.18s (- 0.91%)19.67s19.88sp=0.004 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time24.56s (± 0.12%)24.38s (± 0.42%)-0.18s (- 0.75%)24.21s24.51sp=0.006 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) pack this

@typescript-bot

TypeScript Bot (typescript-bot) commented Mar 31, 2025

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
pack this✅ Started❌ Results

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), something went wrong when looking for the build artifact. (You can check the log here).

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/61505/merge:

Everything looks good!

@ssalbdivad

Copy link
Copy Markdown

This is a set of instantiation benchmarks with results from 5.8.2 for related scenarios.

These can be run against the installed TS version by executing this with node or similar, or could just be used as a reference when constructing benchmarks for TS directly:

import{bench}from"@ark/attest"typemerge<base,props>=Omit<base,keyofprops&keyofbase>&propsdeclareconstmerge: <l,r>(l: l,r: r)=>merge<l,r>bench("functional(10)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})}).types([59386,"instantiations"])bench("functional(11)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})}).types([177537,"instantiations"])bench("functional(12)",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})constk=merge(j,{l: 12})// should be linear relative to functional(10) and functional(11)}).types([531887,"instantiations"])bench("functional(12) with spread",()=>{consta=merge({a: 1},{b: 2})constb=merge(a,{c: 3})constc=merge(b,{d: 4})constd=merge(c,{e: 5})conste=merge(d,{f: 6})constf=merge(e,{g: 7})constg=merge(f,{h: 8})consth=merge(g,{i: 9})consti=merge(h,{j: 10})constj=merge(i,{k: 11})constk=merge(j,{l: 12})return{
...i,
...j,
...k}// should be similar to functional(12)}).types([797849,"instantiations"])typeType<t>={merge: <r>(r: r)=>Type<merge<t,r>>unwrapped: t}declareconsta: Type<{a: 1}>bench("chained(10)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})}).types([178394,"instantiations"])bench("chained(11)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})}).types([532898,"instantiations"])bench("chained(12)",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})constl=k.merge({l: 12})// should be linear relative to chained(10) and chained(11)}).types([1596004,"instantiations"])bench("chained(12) with spread",()=>{constb=a.merge({b: 2})constc=b.merge({c: 3})constd=c.merge({d: 4})conste=d.merge({e: 5})constf=e.merge({f: 6})constg=f.merge({g: 7})consth=g.merge({h: 8})consti=h.merge({i: 9})constj=i.merge({j: 10})constk=j.merge({k: 11})constl=k.merge({l: 12})return{
...i.unwrapped,
...j.unwrapped,
...k.unwrapped}// should be similar to chained(12)}).types([1684778,"instantiations"])

I would expect the instantiation scaling to not be purely linear for this sort of operation as the number of props is increasing, but it should be relatively close. None of these scenarios should result in the exponential scaling we see currently.

I'm also including the original end-to-end type benchmarks that identified this issue here. Once the minimal repo scaling has been addressed, we should sanity check that the scaling here is also fixed:

import{bench}from'@ark/attest';import{initTRPC}from'@trpc/server';import{type}from'arktype';import{z}from'zod';constt=initTRPC.create();// avoid pollution from one-time library setupbench.baseline(()=>{constrouter=t.router({baseline: t.procedure.input(z.object({baseline: z.string(),}),).query(({ input })=>`hello ${input.baseline}`),arkBaseline: t.procedure.input(type({baseline: 'string',}),).query(({ input })=>`hello ${input.baseline}`),});});constbase=z.object({a: z.string(),b: z.string(),c: z.string(),});bench('non-sequential Zod type',async()=>{constnonSequentialRouter=t.router({query1: t.procedure.input(base).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(base).mutation(({ input })=>`hello ${input.a}`),});// this is relatively cheap}).types([1659,'instantiations']);// even though sequential is totally equivalent to nonSequential, its// Zod representation is not reduced and still includes intermediate operationsconstsequential=base.partial().merge(base).pick({a: true,b: true,c: true}).omit({}).merge(base);constbase2=z.object({d: z.string(),e: z.string(),f: z.string(),});bench('isolated sequential zod',()=>{constsequential=base2.partial().merge(base2).pick({d: true,e: true,f: true}).omit({}).merge(base2);// this is expensive}).types([11420,'instantiations']);bench('sequential Zod type',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(sequential).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(sequential).mutation(({ input })=>`hello ${input.a}`),});// but it's in combination with trpc that these sequentially evaluated// Zod types get out of control. instead of incurring a 1-time evaluation// cost, it seems it can't be cached and the extra inference cost// is incurred multiple times (even worse with deepPartial)}).types([49173,'instantiations']);constarkBase=type({a: 'string',b: 'string',c: 'string',});bench('non-sequential arktype',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(arkBase).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(arkBase).mutation(({ input })=>`hello ${input.a}`),});// realtively cheap}).types([2961,'instantiations']);constarkBase2=type({d: 'string',e: 'string',f: 'string',});bench('isolated sequential arktype',()=>{arkBase2.partial().merge(arkBase2).pick('d','e','f').omit().merge(arkBase2);// these kind of operations are much cheaper in ArkType than Zod}).types([2336,'instantiations']);constarkSequential=arkBase.partial().merge(arkBase).pick('a','b','c').omit().merge(arkBase);bench('sequential arktype',async()=>{constsequentialRouter=t.router({query1: t.procedure.input(arkSequential).query(({ input })=>`hello ${input.a}`),mutation1: t.procedure.input(arkSequential).mutation(({ input })=>`hello ${input.a}`),});// even though hovering arkSequential is identical to hovering arkBase,// TS still seems to do a lot of repeated work inferring it somehow (though less than Zod)}).types([17906,'instantiations']);

@Andarist

Copy link
Copy Markdown
ContributorAuthor

David Blass (@ssalbdivad) tested this in a trpc-based repo.

TS 5.8:

Types: 139629
Instantiations: 12133763
Memory used: 798162K
Check time: 13.04s

This branch (commit b0f5c70):

Types: 139629
Instantiations: 817105
Memory used: 819990K
Check time: 6.00s

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it
TypeScript Bot (@typescript-bot) pack this

@typescript-bot

TypeScript Bot (typescript-bot) commented Apr 1, 2025

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
pack this✅ Started✅ Results
test top400✅ Started✅ Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@Andarist

Copy link
Copy Markdown
ContributorAuthor

I'm still slightly concerned that caches might not always be invalidated. For example, if the nonFixingMapper associated with an inference context is combined with another mapper, then the cache of that combined mapper isn't cleared by clearCachedInferences, and it's not easy to see how it could be. This hypothetical issue doesn't show up in any of our tests, but I do think it could possibly happen.

This is a valid concern 👍 I'll see what I can do to address it

@ahejlsberg

Copy link
Copy Markdown
Member

This is a valid concern 👍 I'll see what I can do to address it

You might experiment with clearing all caches whenever a new inference is made. If that doesn't adversely affect performance I think that might be the best option. Also, as I mentioned earlier, you can remove some overhead by only caching results of union and intersection instantiations.

@jakebailey

Copy link
Copy Markdown
Member

TypeScript Bot (@typescript-bot) test it
TypeScript Bot (@typescript-bot) pack this

@typescript-bot

TypeScript Bot (typescript-bot) commented May 2, 2025

Copy link
Copy Markdown
Contributor

Starting jobs; this comment will be updated as builds start and complete.

CommandStatusResults
pack this✅ Started✅ Results
test top400✅ Started✅ Results
user test this✅ Started✅ Results
run dt✅ Started✅ Results
perf test this faster✅ Started👀 Results

@typescript-bot

TypeScript Bot (typescript-bot) commented May 2, 2025

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), I've packed this into an installable tgz. You can install it for testing by referencing it in your package.json like so:

{
"devDependencies": {
"typescript": "https://typescript.visualstudio.com/cf7ac146-d525-443c-b23c-0d58337efebc/_apis/build/builds/165066/artifacts?artifactName=tgz&fileId=78AE6840F3C45DEE689460611669A8466E20BF2A28EC3B53C7765071FF04AB7A02&fileName=/typescript-5.9.0-insiders.20250502.tgz"
}
}

and then running npm install.


There is also a playground for this build and an npm module you can use via "typescript": "npm:@typescript-deploys/pr-build@5.9.0-pr-61505-58".;

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the user tests with tsc comparing main and refs/pull/61505/merge:

There were infrastructure failures potentially unrelated to your change:

  • 1 instance of "Timeout"

Otherwise...

Everything looks good!

@typescript-bot

Copy link
Copy Markdown
Contributor

Hey Jake Bailey (@jakebailey), the results of running the DT tests are ready.

Everything looks the same!

You can check the log here.

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey)
The results of the perf run you requested are in!

Here they are:

tsc

Comparison Report - baseline..pr
MetricbaselineprDeltaBestWorstp-value
Compiler-Unions - node (v18.15.0, x64)
Errors3434~~~p=1.000 n=6
Symbols62,39062,390~~~p=1.000 n=6
Types50,39550,395~~~p=1.000 n=6
Memory used195,358k (± 0.94%)193,632k (± 0.75%)~192,921k196,597kp=0.230 n=6
Parse Time1.31s (± 1.08%)1.31s (± 0.79%)~1.29s1.32sp=0.738 n=6
Bind Time0.73s0.73s~~~p=1.000 n=6
Check Time9.79s (± 0.38%)9.78s (± 0.33%)~9.73s9.81sp=0.517 n=6
Emit Time2.73s (± 0.67%)2.73s (± 0.68%)~2.71s2.75sp=1.000 n=6
Total Time14.55s (± 0.23%)14.54s (± 0.38%)~14.47s14.59sp=1.000 n=6
angular-1 - node (v18.15.0, x64)
Errors5656~~~p=1.000 n=6
Symbols948,670948,670~~~p=1.000 n=6
Types410,947410,947~~~p=1.000 n=6
Memory used1,224,351k (± 0.00%)1,224,412k (± 0.00%)~1,224,360k1,224,514kp=0.128 n=6
Parse Time6.67s (± 0.45%)6.65s (± 0.77%)~6.57s6.70sp=0.628 n=6
Bind Time1.90s (± 0.40%)1.88s (± 0.58%)-0.02s (- 0.97%)1.87s1.89sp=0.015 n=6
Check Time31.90s (± 0.24%)32.10s (± 0.30%)+0.20s (+ 0.64%)31.96s32.23sp=0.010 n=6
Emit Time15.23s (± 0.90%)15.28s (± 0.36%)~15.21s15.37sp=0.573 n=6
Total Time55.71s (± 0.15%)55.92s (± 0.17%)+0.22s (+ 0.39%)55.82s56.07sp=0.005 n=6
mui-docs - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols2,361,6672,361,667~~~p=1.000 n=6
Types868,625868,625~~~p=1.000 n=6
Memory used2,317,105k (± 0.00%)2,318,136k (± 0.00%)+1,030k (+ 0.04%)2,318,073k2,318,171kp=0.005 n=6
Parse Time8.76s (± 0.21%)8.76s (± 0.29%)~8.73s8.80sp=0.465 n=6
Bind Time2.21s (± 0.78%)2.21s (± 1.01%)~2.19s2.25sp=0.934 n=6
Check Time69.90s (± 0.33%)69.85s (± 0.33%)~69.63s70.19sp=0.748 n=6
Emit Time0.30s (± 2.11%)0.30s (± 3.40%)~0.29s0.32sp=0.654 n=6
Total Time81.17s (± 0.30%)81.12s (± 0.28%)~80.89s81.46sp=0.630 n=6
self-build-src - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,228,0441,228,093+49 (+ 0.00%)~~p=0.001 n=6
Types267,260267,268+8 (+ 0.00%)~~p=0.001 n=6
Memory used2,973,179k (±10.03%)2,608,144k (±14.46%)~2,363,403k3,095,909kp=0.230 n=6
Parse Time6.69s (± 1.32%)6.62s (± 1.46%)~6.50s6.75sp=0.173 n=6
Bind Time2.18s (± 2.19%)2.17s (± 0.95%)~2.13s2.19sp=0.936 n=6
Check Time42.87s (± 0.43%)42.90s (± 0.46%)~42.70s43.21sp=0.936 n=6
Emit Time3.43s (± 3.59%)3.56s (± 5.02%)~3.34s3.85sp=0.230 n=6
Total Time55.17s (± 0.47%)55.27s (± 0.42%)~54.96s55.47sp=0.471 n=6
self-build-src-public-api - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols1,228,0441,228,093+49 (+ 0.00%)~~p=0.001 n=6
Types267,260267,268+8 (+ 0.00%)~~p=0.001 n=6
Memory used2,427,922k (± 0.03%)2,427,536k (± 0.01%)~2,427,071k2,428,053kp=0.298 n=6
Parse Time5.46s (± 0.88%)5.47s (± 0.74%)~5.40s5.51sp=0.573 n=6
Bind Time1.83s (± 1.02%)1.81s (± 1.03%)~1.79s1.84sp=0.331 n=6
Check Time35.40s (± 0.51%)35.52s (± 0.19%)~35.46s35.64sp=0.378 n=6
Emit Time3.04s (± 0.69%)3.06s (± 2.16%)~2.99s3.15sp=1.000 n=6
Total Time45.73s (± 0.47%)45.88s (± 0.29%)~45.75s46.08sp=0.298 n=6
self-compiler - node (v18.15.0, x64)
Errors00~~~p=1.000 n=6
Symbols263,411263,460+49 (+ 0.02%)~~p=0.001 n=6
Types107,096107,104+8 (+ 0.01%)~~p=0.001 n=6
Memory used441,780k (± 0.03%)441,847k (± 0.02%)~441,725k441,909kp=0.128 n=6
Parse Time3.55s (± 0.81%)3.53s (± 0.85%)~3.50s3.57sp=0.515 n=6
Bind Time1.31s (± 0.39%)1.31s (± 0.93%)~1.29s1.32sp=0.351 n=6
Check Time18.96s (± 0.42%)19.05s (± 0.33%)~18.98s19.12sp=0.064 n=6
Emit Time1.53s (± 1.64%)1.53s (± 0.72%)~1.52s1.55sp=0.805 n=6
Total Time25.35s (± 0.27%)25.42s (± 0.40%)~25.30s25.53sp=0.378 n=6
ts-pre-modules - node (v18.15.0, x64)
Errors7171~~~p=1.000 n=6
Symbols225,981225,981~~~p=1.000 n=6
Types94,35694,356~~~p=1.000 n=6
Memory used371,253k (± 0.01%)371,323k (± 0.03%)~371,253k371,531kp=0.109 n=6
Parse Time2.89s (± 1.21%)2.89s (± 0.92%)~2.84s2.92sp=0.872 n=6
Bind Time1.58s (± 1.67%)1.59s (± 1.63%)~1.55s1.62sp=0.629 n=6
Check Time16.50s (± 0.27%)16.55s (± 0.26%)~16.49s16.59sp=0.147 n=6
Emit Time0.00s0.00s (±244.70%)~0.00s0.01sp=0.405 n=6
Total Time20.98s (± 0.30%)21.02s (± 0.17%)~20.98s21.07sp=0.148 n=6
vscode - node (v18.15.0, x64)
Errors44~~~p=1.000 n=6
Symbols3,391,6493,391,649~~~p=1.000 n=6
Types1,148,5941,148,594~~~p=1.000 n=6
Memory used3,443,382k (± 0.00%)3,443,190k (± 0.01%)~3,442,813k3,443,554kp=0.230 n=6
Parse Time14.69s (± 0.92%)14.70s (± 0.25%)~14.66s14.75sp=0.627 n=6
Bind Time5.01s (±11.96%)4.72s (± 0.39%)~4.70s4.75sp=0.569 n=6
Check Time92.54s (± 0.72%)94.92s (± 2.83%)~92.17s99.26sp=0.078 n=6
Emit Time29.79s (± 1.08%)30.06s (± 2.03%)~29.50s31.15sp=0.423 n=6
Total Time142.03s (± 0.68%)144.41s (± 1.93%)~141.31s148.14sp=0.128 n=6
webpack - node (v18.15.0, x64)
Errors22~~~p=1.000 n=6
Symbols317,345317,345~~~p=1.000 n=6
Types140,331140,331~~~p=1.000 n=6
Memory used472,396k (± 0.03%)472,299k (± 0.02%)~472,200k472,471kp=0.173 n=6
Parse Time5.16s (± 0.54%)5.17s (± 0.82%)~5.09s5.20sp=0.418 n=6
Bind Time2.25s (± 1.12%)2.25s (± 1.42%)~2.20s2.30sp=0.934 n=6
Check Time25.79s (± 0.56%)25.89s (± 0.34%)~25.78s26.00sp=0.173 n=6
Emit Time0.00s (±244.70%)0.00s (±244.70%)~0.00s0.01sp=1.000 n=6
Total Time33.20s (± 0.43%)33.32s (± 0.26%)~33.18s33.38sp=0.065 n=6
xstate-main - node (v18.15.0, x64)
Errors55~~~p=1.000 n=6
Symbols570,517570,517~~~p=1.000 n=6
Types191,417191,417~~~p=1.000 n=6
Memory used500,886k (± 0.03%)502,543k (± 0.01%)+1,657k (+ 0.33%)502,444k502,603kp=0.005 n=6
Parse Time4.32s (± 0.73%)4.31s (± 0.31%)~4.30s4.33sp=0.801 n=6
Bind Time1.53s (± 1.13%)1.53s (± 0.72%)~1.51s1.54sp=0.933 n=6
Check Time25.09s (± 1.86%)24.52s (± 0.27%)-0.57s (- 2.27%)24.44s24.59sp=0.005 n=6
Emit Time0.00s0.00s~~~p=1.000 n=6
Total Time30.93s (± 1.59%)30.36s (± 0.24%)-0.57s (- 1.84%)30.28s30.44sp=0.005 n=6
System info unknown
Hosts
  • node (v18.15.0, x64)
Scenarios
  • Compiler-Unions - node (v18.15.0, x64)
  • angular-1 - node (v18.15.0, x64)
  • mui-docs - node (v18.15.0, x64)
  • self-build-src - node (v18.15.0, x64)
  • self-build-src-public-api - node (v18.15.0, x64)
  • self-compiler - node (v18.15.0, x64)
  • ts-pre-modules - node (v18.15.0, x64)
  • vscode - node (v18.15.0, x64)
  • webpack - node (v18.15.0, x64)
  • xstate-main - node (v18.15.0, x64)
BenchmarkNameIterations
Currentpr6
Baselinebaseline6

Developer Information:

Download Benchmarks

@typescript-bot

Copy link
Copy Markdown
Contributor

Jake Bailey (@jakebailey) Here are the results of running the top 400 repos with tsc comparing main and refs/pull/61505/merge:

Everything looks good!

@Andarist

Mateusz Burzyński (Andarist) commented May 6, 2025

Copy link
Copy Markdown
ContributorAuthor

You might experiment with clearing all caches whenever a new inference is made. If that doesn't adversely affect performance I think that might be the best option.

I've followed this advice and the numbers are still pretty much the same as the ones I mentioned here. Instantiations went up a little bit but by such a negligible number (+341), it doesn't matter at all.

Also, as I mentioned earlier, you can remove some overhead by only caching results of union and intersection instantiations.

The current version doesn't come with any noticeable overhead, from what I can tell. It seems to me doing this just for union/intersections would only complicate the code right now. If you feel strongly about it though, I could certainly run an extra experiment like this.

@jakebaileyJake Bailey (jakebailey) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So, I feel like this must be good to go at this point. The code looks pretty simple, the benchmarks are good, and it should be straightforward to port into the Go codebase (eventually) as there aren't any goofy uses of maps / prototype modification anymore.

Are there any remaining concerns here?

@mikeduminy

Copy link
Copy Markdown

This was a game-changer PR for us. Thank you!

BeforeAfterImprovement
Instantiations704,556,46710,119,79398.56%
Check time395.48132.0966.60%
Total time425.86164.7761.31%

@jakebailey

Copy link
Copy Markdown
Member

What repo is that? Is it public?

@mikeduminy

Mike Duminy (mikeduminy) commented Jul 22, 2025

Copy link
Copy Markdown

It's a private repo unfortunately. It is for the Klarna app (react-native, web, etc). But we'd be happy to test out anything internally and get back to you if want 😄

@jakebailey

Jake Bailey (jakebailey) commented Jul 22, 2025

Copy link
Copy Markdown
Member

I want a public repo for our benchmarks, unfortunately

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

For Uncommitted BugPR for untriaged, rejected, closed or missing bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

10 participants

@Andarist@typescript-bot@jakebailey@ssalbdivad@ahejlsberg@fxdave@gavbaa@colinhacks@mikeduminy@andrewbranch