Skip to content

Inline storage for small symbol tables - #64996

Draft
Jake Bailey (jakebailey) wants to merge 2 commits into
microsoft:mainfrom
jakebailey:inline-sym-tab
Draft

Jake Bailey (jakebailey) wants to merge 2 commits into
microsoft:mainfrom
jakebailey:inline-sym-tab

Conversation

@jakebailey

Copy link
Copy Markdown
Member

This is something I brought up in review; inline some space for small tables.

I thought 1 would be right, but doing some stats on our tests, 4 appeared to be the magic number.

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │               sec/op               │    sec/op     vs base              │
Bind/checker.ts-20                                 25.86m ± 14%   24.61m ±  1%  -4.82% (p=0.004 n=6)
Bind/dom.generated.d.ts-20                         11.21m ±  6%   10.68m ±  6%       ~ (p=0.093 n=6)
Bind/Herebyfile.mjs-20                             406.6µ ± 28%   326.9µ ± 19%       ~ (p=0.240 n=6)
geomean                                            4.902m         4.413m        -9.99%

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │                B/op                │     B/op      vs base              │
Bind/checker.ts-20                                 5.634Mi ± 0%   5.677Mi ± 0%  +0.76% (p=0.002 n=6)
Bind/dom.generated.d.ts-20                         3.363Mi ± 0%   3.400Mi ± 0%  +1.09% (p=0.002 n=6)
Bind/Herebyfile.mjs-20                             102.5Ki ± 0%   102.5Ki ± 0%  +0.02% (p=0.002 n=6)
geomean                                            1.238Mi        1.246Mi       +0.62%

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │             allocs/op              │  allocs/op   vs base               │
Bind/checker.ts-20                                  25.78k ± 0%   18.30k ± 0%  -28.98% (p=0.002 n=6)
Bind/dom.generated.d.ts-20                          24.45k ± 0%   15.44k ± 0%  -36.86% (p=0.002 n=6)
Bind/Herebyfile.mjs-20                               571.0 ± 0%    375.0 ± 0%  -34.33% (p=0.002 n=6)
geomean                                             7.113k        4.733k       -33.47%

Small symbol tables need not allocate their entries separately. Across
4.71 million tables in the compiler suite, 36.3% peaked at four symbols
or fewer and 62.1% peaked at eight or fewer.

Four slots offer a better allocation/space tradeoff than eight: binding
allocations fall by 29-37%, while allocated bytes increase by at most
1.1%. Eight slots increased allocated bytes by 3-6%. Six slots cause
unfavorable slice growth for larger tables.

Binding benchmarks, with default GC settings:

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │               sec/op               │    sec/op     vs base              │
Bind/checker.ts-20                                 25.86m ± 14%   24.61m ±  1%  -4.82% (p=0.004 n=6)
Bind/dom.generated.d.ts-20                         11.21m ±  6%   10.68m ±  6%       ~ (p=0.093 n=6)
Bind/Herebyfile.mjs-20                             406.6µ ± 28%   326.9µ ± 19%       ~ (p=0.240 n=6)
geomean                                            4.902m         4.413m        -9.99%

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │                B/op                │     B/op      vs base              │
Bind/checker.ts-20                                 5.634Mi ± 0%   5.677Mi ± 0%  +0.76% (p=0.002 n=6)
Bind/dom.generated.d.ts-20                         3.363Mi ± 0%   3.400Mi ± 0%  +1.09% (p=0.002 n=6)
Bind/Herebyfile.mjs-20                             102.5Ki ± 0%   102.5Ki ± 0%  +0.02% (p=0.002 n=6)
geomean                                            1.238Mi        1.246Mi       +0.62%

                           │ built/symbol-table-bind-before.txt │  built/symbol-table-bind-four.txt  │
                           │             allocs/op              │  allocs/op   vs base               │
Bind/checker.ts-20                                  25.78k ± 0%   18.30k ± 0%  -28.98% (p=0.002 n=6)
Bind/dom.generated.d.ts-20                          24.45k ± 0%   15.44k ± 0%  -36.86% (p=0.002 n=6)
Bind/Herebyfile.mjs-20                               571.0 ± 0%    375.0 ± 0%  -34.33% (p=0.002 n=6)
geomean                                             7.113k        4.733k       -33.47%
Copilot AI balanced review requested due to automatic review settings October 28, 2026 17:42
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Oct 28, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Focused tests are missing for the inline-storage boundaries, particularly symbol preservation when growing from four to five entries and independent backing storage when mutating small clones and their originals.

1 open issue
What changed in this PR

This PR reduces allocations in the native compiler’s symbol tables by storing up to four symbol pointers inline.

Changes:

  • Use inline storage for small tables during construction and insertion.
  • Clear inline references when entries grow beyond that storage.
  • Give small clones their own inline backing storage.
File Description
tsc/​internal/​ast/​symbol.go Adds four-entry inline storage and updates table construction, growth, and cloning.

Review effort: Balanced

Comment on lines +292 to +294
if len(m.entries) == len(m.symbols)+1 {
// Once entries outgrow inline storage, don't retain the copied symbols.
clear(m.symbols[:])

Copilot AI commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

One or more custom setup steps configured for this repository failed during this Copilot code review run:

Run {

Setup steps run before each review. If the review above is missing context, or no review was posted at all, the failing step above may be the cause. See the workflow run for failure details, fix your setup steps configuration, and re-request a review.

Note

You can configure setup steps for Copilot code review separately from Copilot cloud agent with a copilot-code-review.yml file. Read the docs for details.

@jakebailey Jake Bailey (jakebailey) added the Domain: Performance Reports of unusually slow behavior label Oct 28, 2026
@jakebailey

Copy link
Copy Markdown
Member Author

TypeScript Bot (@typescript-bot) perf test this faster

@typescript-automation

typescript-automation Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

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

Command Status Results
perf test this faster ✅ Started 👀 Results

@typescript-automation

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
Metric baseline pr Delta Best Worst p-value
Compiler-Unions - native
Errors 41 41 ~ ~ ~ p=1.000 n=20
Symbols 88,466 88,466 ~ ~ ~ p=1.000 n=20
Types 79,607 79,607 ~ ~ ~ p=1.000 n=20
Memory Used 104,133k (± 0.03%) 104,345k (± 0.03%) +212k (+ 0.20%) 104,252k 104,487k p=0.000 n=20
Memory Allocs 2,200,862 (± 0.00%) 2,183,700 (± 0.00%) -17,162 (- 0.78%) 2,183,385 2,184,117 p=0.000 n=20
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Parse Time 0.025s (± 2.87%) 0.025s (± 2.52%) ~ 0.023s 0.028s p=0.407 n=20
Bind Time 0.011s (± 8.37%) 0.010s (± 6.58%) ~ 0.008s 0.013s p=0.144 n=20
Check Time 0.259s (± 2.52%) 0.258s (± 1.35%) ~ 0.246s 0.273s p=0.984 n=20
Emit Time 0.181s (± 3.19%) 0.182s (± 2.79%) ~ 0.171s 0.222s p=0.533 n=20
Total Time 0.481s (± 1.96%) 0.478s (± 1.45%) ~ 0.455s 0.526s p=0.920 n=20
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=20
Symbols 586,508 (± 0.01%) 586,534 (± 0.02%) ~ 586,125 586,828 p=0.457 n=20
Types 241,692 (± 0.00%) 241,692 (± 0.00%) ~ 241,688 241,704 p=0.937 n=20
Memory Used 518,013k (± 0.02%) 520,494k (± 0.02%) +2,481k (+ 0.48%) 520,213k 520,926k p=0.000 n=20
Memory Allocs 13,119,129 (± 0.04%) 12,932,198 (± 0.05%) -186,931 (- 1.42%) 12,923,831 12,974,539 p=0.000 n=20
Config Time 0.009s (± 2.82%) 0.008s (± 2.82%) ~ 0.008s 0.009s p=1.000 n=20
Parse Time 0.132s (± 2.78%) 0.136s (± 4.21%) ~ 0.122s 0.170s p=0.336 n=20
Bind Time 0.044s (± 9.65%) 0.040s (± 8.77%) 🟩-0.004s (- 8.83%) 0.036s 0.058s p=0.000 n=20
Check Time 0s 0s ~ ~ ~ p=1.000 n=20
Emit Time 0.910s (± 0.99%) 0.917s (± 1.00%) ~ 0.885s 0.974s p=0.372 n=20
Total Time 1.106s (± 0.74%) 1.113s (± 0.93%) ~ 1.078s 1.169s p=0.358 n=20
arktype - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 685,943 685,943 ~ ~ ~ p=1.000 n=20
Types 939,490 939,490 ~ ~ ~ p=1.000 n=20
Memory Used 497,283k (± 0.01%) 499,921k (± 0.01%) +2,638k (+ 0.53%) 499,781k 500,158k p=0.000 n=20
Memory Allocs 12,804,673 (± 0.01%) 12,643,096 (± 0.01%) -161,577 (- 1.26%) 12,640,477 12,656,844 p=0.000 n=20
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=20
Parse Time 0.042s (± 2.55%) 0.042s (± 3.16%) ~ 0.038s 0.048s p=0.835 n=20
Bind Time 0.013s (± 8.63%) 0.012s (± 7.37%) 🟩-0.001s (- 8.37%) 0.010s 0.018s p=0.032 n=20
Check Time 0.978s (± 0.62%) 0.982s (± 0.90%) ~ 0.956s 1.023s p=0.804 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 1.064s (± 0.57%) 1.066s (± 0.81%) ~ 1.041s 1.109s p=0.995 n=20
class-transformer - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 45,376 45,376 ~ ~ ~ p=1.000 n=20
Types 7,573 7,573 ~ ~ ~ p=1.000 n=20
Memory Used 27,835k (± 0.02%) 28,076k (± 0.02%) +241k (+ 0.87%) 28,060k 28,100k p=0.000 n=20
Memory Allocs 323,353 (± 0.04%) 303,340 (± 0.04%) 🟩-20,013 (- 6.19%) 302,934 304,124 p=0.000 n=20
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Parse Time 0.009s (± 2.99%) 0.009s (± 2.99%) ~ 0.008s 0.010s p=0.876 n=20
Bind Time 0.002s (±13.23%) 0.002s (±11.63%) ~ 0.002s 0.004s p=0.965 n=20
Check Time 0.008s (± 5.79%) 0.008s (± 4.86%) ~ 0.007s 0.010s p=0.921 n=20
Emit Time 0.010s (± 3.24%) 0.010s (± 3.06%) ~ 0.008s 0.011s p=0.635 n=20
Total Time 0.031s (± 1.37%) 0.031s (± 1.34%) ~ 0.029s 0.033s p=0.700 n=20
effect - native
Errors 12 12 ~ ~ ~ p=1.000 n=20
Symbols 1,235,117 1,235,117 ~ ~ ~ p=1.000 n=20
Types 894,115 894,115 ~ ~ ~ p=1.000 n=20
Memory Used 940,524k (± 0.02%) 944,659k (± 0.02%) +4,134k (+ 0.44%) 944,219k 945,256k p=0.000 n=20
Memory Allocs 15,752,915 (± 0.01%) 15,398,075 (± 0.01%) -354,840 (- 2.25%) 15,387,991 15,405,602 p=0.000 n=20
Config Time 0.002s (± 5.09%) 0.002s (± 5.09%) ~ 0.002s 0.003s p=1.000 n=20
Parse Time 0.152s (± 1.27%) 0.152s (± 1.94%) ~ 0.142s 0.163s p=0.559 n=20
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Check Time 0.981s (± 1.14%) 0.988s (± 1.22%) ~ 0.932s 1.020s p=0.256 n=20
Emit Time 0.528s (± 2.02%) 0.522s (± 1.47%) ~ 0.494s 0.552s p=0.180 n=20
Total Time 1.755s (± 0.98%) 1.762s (± 0.70%) ~ 1.717s 1.807s p=0.625 n=20
mui-docs - native
Errors 11,405 11,405 ~ ~ ~ p=1.000 n=20
Symbols 3,784,027 3,784,027 ~ ~ ~ p=1.000 n=20
Types 1,355,982 1,355,982 ~ ~ ~ p=1.000 n=20
Memory Used 1,836,190k (± 0.07%) 1,843,309k (± 0.06%) +7,119k (+ 0.39%) 1,840,072k 1,847,216k p=0.000 n=20
Memory Allocs 26,752,056 (± 0.08%) 26,078,435 (± 0.08%) -673,621 (- 2.52%) 26,014,840 26,203,354 p=0.000 n=20
Config Time 0.008s (± 2.82%) 0.009s (± 2.78%) ~ 0.008s 0.009s p=0.752 n=20
Parse Time 0.303s (± 1.61%) 0.303s (± 2.55%) ~ 0.265s 0.338s p=0.394 n=20
Bind Time 0.001s 0.001s ~ ~ ~ p=1.000 n=20
Check Time 3.257s (± 1.20%) 3.315s (± 0.97%) +0.058s (+ 1.78%) 3.223s 3.463s p=0.005 n=20
Emit Time 0.253s (± 1.58%) 0.255s (± 2.26%) ~ 0.236s 0.274s p=0.533 n=20
Total Time 4.312s (± 1.11%) 4.383s (± 1.22%) +0.071s (+ 1.64%) 4.238s 4.612s p=0.028 n=20
sentry - native
Errors 2 2 ~ ~ ~ p=1.000 n=20
Symbols 4,793,249 4,793,249 ~ ~ ~ p=1.000 n=20
Types 2,607,051 2,607,051 ~ ~ ~ p=1.000 n=20
Memory Used 2,880,968k (± 0.03%) 2,897,041k (± 0.03%) +16,073k (+ 0.56%) 2,892,829k 2,901,999k p=0.000 n=20
Memory Allocs 75,968,304 (± 0.10%) 74,451,871 (± 0.06%) -1,516,434 (- 2.00%) 74,249,051 74,624,609 p=0.000 n=20
Config Time 0.026s (± 1.15%) 0.025s (± 1.29%) 🟩-0.001s (- 3.45%) 0.024s 0.026s p=0.000 n=20
Parse Time 0.486s (± 3.78%) 0.461s (± 2.49%) 🟩-0.025s (- 5.13%) 0.430s 0.523s p=0.012 n=20
Bind Time 0.164s (±10.19%) 0.173s (± 9.72%) ~ 0.125s 0.214s p=0.941 n=20
Check Time 7.191s (± 0.80%) 7.075s (± 1.10%) -0.116s (- 1.62%) 6.801s 7.408s p=0.015 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 7.921s (± 0.76%) 7.789s (± 1.03%) -0.132s (- 1.67%) 7.527s 8.098s p=0.021 n=20
social-app - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 1,883,075 1,883,075 ~ ~ ~ p=1.000 n=20
Types 1,158,171 1,158,171 ~ ~ ~ p=1.000 n=20
Memory Used 972,607k (± 0.02%) 977,435k (± 0.01%) +4,828k (+ 0.50%) 976,891k 977,947k p=0.000 n=20
Memory Allocs 13,710,976 (± 0.11%) 13,299,363 (± 0.10%) 🟩-411,613 (- 3.00%) 13,259,349 13,341,778 p=0.000 n=20
Config Time 0.008s (± 1.31%) 0.008s (± 2.98%) 🟩-0.000s (- 3.77%) 0.007s 0.008s p=0.044 n=20
Parse Time 0.162s (± 2.75%) 0.163s (± 3.63%) ~ 0.150s 0.209s p=0.952 n=20
Bind Time 0.045s (±10.21%) 0.044s (± 9.72%) ~ 0.036s 0.075s p=0.430 n=20
Check Time 1.611s (± 0.82%) 1.620s (± 1.77%) ~ 1.545s 1.814s p=0.888 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 1.847s (± 0.83%) 1.856s (± 1.61%) ~ 1.786s 2.057s p=0.794 n=20
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 1,184,976 1,184,976 ~ ~ ~ p=1.000 n=20
Types 438,967 438,967 ~ ~ ~ p=1.000 n=20
Memory Used 1,171,337k (± 0.27%) 1,181,132k (± 0.32%) +9,795k (+ 0.84%) 1,163,971k 1,198,138k p=0.000 n=20
Memory Allocs 77,473,363 (± 0.14%) 76,338,875 (± 0.12%) -1,134,488 (- 1.46%) 75,863,626 76,613,398 p=0.000 n=20
Config Time 0.002s (±17.44%) 0.002s (±18.96%) ~ 0.000s 0.002s p=0.509 n=20
Parse Time 0.146s (± 2.18%) 0.142s (± 1.22%) ~ 0.135s 0.148s p=0.114 n=20
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Check Time 0.861s (± 0.82%) 0.870s (± 1.10%) ~ 0.839s 0.929s p=0.133 n=20
Emit Time 0.185s (± 1.24%) 0.183s (± 2.69%) ~ 0.169s 0.206s p=0.596 n=20
Total Time 10.421s (± 0.99%) 10.424s (± 0.63%) ~ 10.205s 10.897s p=0.461 n=20
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 263,513 263,513 ~ ~ ~ p=1.000 n=20
Types 196,118 196,118 ~ ~ ~ p=1.000 n=20
Memory Used 224,612k (± 0.02%) 225,361k (± 0.02%) +749k (+ 0.33%) 225,195k 225,533k p=0.000 n=20
Memory Allocs 4,304,502 (± 0.01%) 4,243,524 (± 0.01%) -60,978 (- 1.42%) 4,242,281 4,244,819 p=0.000 n=20
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Parse Time 0.052s (± 3.71%) 0.052s (± 3.27%) ~ 0.046s 0.058s p=0.773 n=20
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Check Time 0.408s (± 1.07%) 0.410s (± 1.29%) ~ 0.394s 0.436s p=0.733 n=20
Emit Time 0.082s (± 6.08%) 0.080s (± 2.06%) ~ 0.074s 0.089s p=0.438 n=20
Total Time 0.571s (± 1.37%) 0.574s (± 1.08%) ~ 0.554s 0.601s p=0.143 n=20
t3code-server - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 4,350,705 4,350,705 ~ ~ ~ p=1.000 n=20
Types 3,040,691 3,040,691 ~ ~ ~ p=1.000 n=20
Memory Used 2,434,913k (± 0.03%) 2,447,696k (± 0.04%) +12,783k (+ 0.52%) 2,445,170k 2,455,023k p=0.000 n=20
Memory Allocs 37,880,948 (± 0.03%) 36,678,940 (± 0.05%) 🟩-1,202,008 (- 3.17%) 36,611,621 36,748,830 p=0.000 n=20
Config Time 0.003s 0.003s ~ ~ ~ p=1.000 n=20
Parse Time 0.277s (± 2.57%) 0.282s (± 2.53%) ~ 0.253s 0.326s p=0.380 n=20
Bind Time 0.101s (± 8.47%) 0.098s (±10.06%) ~ 0.077s 0.138s p=0.218 n=20
Check Time 8.348s (± 0.54%) 8.419s (± 0.98%) ~ 8.141s 8.855s p=0.087 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 8.741s (± 0.55%) 8.813s (± 0.97%) ~ 8.524s 9.272s p=0.149 n=20
tanstack-router - native
Errors 1 1 ~ ~ ~ p=1.000 n=20
Symbols 571,760 571,760 ~ ~ ~ p=1.000 n=20
Types 397,693 397,693 ~ ~ ~ p=1.000 n=20
Memory Used 331,393k (± 0.01%) 333,282k (± 0.01%) +1,889k (+ 0.57%) 333,107k 333,455k p=0.000 n=20
Memory Allocs 5,092,836 (± 0.07%) 4,953,349 (± 0.03%) -139,487 (- 2.74%) 4,948,968 4,963,883 p=0.000 n=20
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=20
Parse Time 0.039s (± 4.24%) 0.041s (± 3.69%) ~ 0.035s 0.045s p=0.205 n=20
Bind Time 0.012s (± 9.87%) 0.012s (± 6.55%) ~ 0.010s 0.017s p=0.301 n=20
Check Time 0.581s (± 0.73%) 0.577s (± 0.83%) ~ 0.564s 0.603s p=0.095 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 0.659s (± 0.73%) 0.658s (± 0.84%) ~ 0.638s 0.683s p=0.846 n=20
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=20
Symbols 231,617 231,617 ~ ~ ~ p=1.000 n=20
Types 181,196 181,196 ~ ~ ~ p=1.000 n=20
Memory Used 193,539k (± 0.02%) 194,206k (± 0.02%) +667k (+ 0.34%) 194,042k 194,395k p=0.000 n=20
Memory Allocs 1,373,062 (± 0.01%) 1,322,498 (± 0.01%) 🟩-50,564 (- 3.68%) 1,321,943 1,323,237 p=0.000 n=20
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Parse Time 0.043s (± 2.84%) 0.043s (± 3.15%) ~ 0.040s 0.049s p=0.855 n=20
Bind Time 0.021s (± 4.54%) 0.021s (± 5.30%) ~ 0.018s 0.026s p=0.603 n=20
Check Time 0.307s (± 1.09%) 0.306s (± 0.91%) ~ 0.296s 0.313s p=0.401 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 0.377s (± 0.73%) 0.376s (± 0.57%) ~ 0.365s 0.387s p=0.316 n=20
vscode - native
Errors 10 10 ~ ~ ~ p=1.000 n=20
Symbols 7,383,152 7,383,152 ~ ~ ~ p=1.000 n=20
Types 4,366,356 4,366,356 ~ ~ ~ p=1.000 n=20
Memory Used 5,134,274k (± 0.02%) 5,159,414k (± 0.01%) +25,140k (+ 0.49%) 5,156,278k 5,163,040k p=0.000 n=20
Memory Allocs 64,779,055 (± 0.01%) 62,222,520 (± 0.03%) 🟩-2,556,535 (- 3.95%) 62,197,008 62,322,193 p=0.000 n=20
Config Time 0.041s (± 1.68%) 0.041s (± 1.69%) ~ 0.038s 0.043s p=0.950 n=20
Parse Time 1.028s (± 1.99%) 1.019s (± 2.32%) ~ 0.902s 1.090s p=0.836 n=20
Bind Time 0.358s (± 2.36%) 0.358s (± 6.97%) ~ 0.307s 0.551s p=0.058 n=20
Check Time 8.226s (± 1.09%) 8.205s (± 1.39%) ~ 7.665s 8.584s p=0.931 n=20
Emit Time 3.224s (± 4.69%) 3.241s (± 5.28%) ~ 2.841s 4.122s p=0.826 n=20
Total Time 12.940s (± 1.54%) 12.928s (± 1.37%) ~ 12.144s 13.768s p=0.941 n=20
webpack - native
Errors 619 619 ~ ~ ~ p=1.000 n=20
Symbols 924,267 924,267 ~ ~ ~ p=1.000 n=20
Types 646,050 646,050 ~ ~ ~ p=1.000 n=20
Memory Used 695,123k (± 0.01%) 698,994k (± 0.01%) +3,871k (+ 0.56%) 698,622k 699,352k p=0.000 n=20
Memory Allocs 6,682,291 (± 0.03%) 6,385,203 (± 0.02%) 🟩-297,088 (- 4.45%) 6,379,757 6,393,571 p=0.000 n=20
Config Time 0.004s (± 3.50%) 0.004s (± 2.58%) ~ 0.004s 0.005s p=1.000 n=20
Parse Time 0.166s (± 3.90%) 0.161s (± 4.13%) ~ 0.140s 0.199s p=0.175 n=20
Bind Time 0.053s (±10.00%) 0.049s (± 3.64%) ~ 0.042s 0.059s p=0.348 n=20
Check Time 1.493s (± 0.95%) 1.493s (± 1.24%) ~ 1.434s 1.588s p=0.815 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 1.743s (± 1.10%) 1.733s (± 1.32%) ~ 1.652s 1.854s p=0.417 n=20
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=20
Symbols 640,494 640,494 ~ ~ ~ p=1.000 n=20
Types 387,992 387,992 ~ ~ ~ p=1.000 n=20
Memory Used 350,935k (± 0.02%) 353,705k (± 0.02%) +2,770k (+ 0.79%) 353,490k 354,251k p=0.000 n=20
Memory Allocs 4,063,634 (± 0.07%) 3,879,588 (± 0.10%) 🟩-184,046 (- 4.53%) 3,867,998 3,903,935 p=0.000 n=20
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=20
Parse Time 0.061s (± 2.83%) 0.060s (± 1.72%) ~ 0.056s 0.065s p=0.813 n=20
Bind Time 0.020s (±10.77%) 0.020s (±11.50%) ~ 0.016s 0.032s p=0.293 n=20
Check Time 0.420s (± 1.08%) 0.425s (± 2.97%) ~ 0.409s 0.537s p=0.952 n=20
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=20
Total Time 0.507s (± 0.83%) 0.510s (± 2.62%) ~ 0.492s 0.628s p=0.587 n=20
System info unknown
Hosts
  • native
Scenarios
  • Compiler-Unions - native
  • angular-1 - native
  • arktype - native
  • class-transformer - native
  • effect - native
  • mui-docs - native
  • sentry - native
  • social-app - native
  • strada-build-src - native
  • strada-compiler - native
  • t3code-server - native
  • tanstack-router - native
  • ts-pre-modules - native
  • vscode - native
  • webpack - native
  • xstate-main - native
Benchmark Name Iterations
Current pr 20
Baseline baseline 20

Developer Information:

Download Benchmarks

@jakebailey

Copy link
Copy Markdown
Member Author

Definitely fewer allocs but a bit unclear how much it matters.

@ahejlsberg

Copy link
Copy Markdown
Member

Yeah, not sure this one is worth it.

@DanielRosenwasser

Daniel Rosenwasser (DanielRosenwasser) commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Here's a thought - lots of symbol tables are just empty, or have fewer than 4 elements, or whatever. You are pre-allocating storage for small symbol tables, but ultimately the optimization here has a member pointing back to another member in the containing type just to avoid a second allocation and give you a bit more locality. Nobody ever needs to look at symbols again except for Set, and occasionally this will over-allocate.

I wonder if we could work off of the stack and then decide on a variant of SymbolMaps only as soon the map needs to escape.

type SymbolMap struct {
	entries []*Symbol
	index   []int32 // open addressing table of entry positions plus one (0 is empty), only past symbolTableLinearMax
}

type SymbolMap1Element {
	SymbolMap
	symbols [1]*Symbol
}

type SymbolMap2Elements {
	SymbolMap
	symbols [2]*Symbol
}

type SymbolMap3Elements {
	SymbolMap
	symbols [3]*Symbol
}

type SymbolMap4Elements {
	SymbolMap
	symbols [4]*Symbol
}

When the SymbolMap has to escape from the stack, we would decide on a suitable representation, which would occasionally just be SymbolMap (either 0 elements or >4 or whatever).

@ahejlsberg

Copy link
Copy Markdown
Member

I wonder if we could work off of the stack and then decide on a variant of SymbolMaps only as soon the map needs to escape.

The thing is, the vast majority of symbol tables escape to the heap because they're attached to AST nodes or symbols. So you might as well put them there to begin with.

@jakebailey

Copy link
Copy Markdown
Member Author

Yeah, I just tried, and it doesn't seem to help (or makes things worse), because the compiler just isn't smart enough.

@DanielRosenwasser

Copy link
Copy Markdown
Member

The thing is, the vast majority of symbol tables escape to the heap because they're attached to AST nodes or symbols.

I meant more like using the stack as temporary storage and allocating the precise size after (similar to what I think the parser does).

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.

The inline-storage growth and clone paths look correct. Existing AST tests and a focused overlay harness covering 42 capacity/size combinations pass, including independent mutations of clones.

@github-project-automation github-project-automation Bot moved this from Not started to Needs merge in PR Backlog Oct 30, 2026
@ahejlsberg

Copy link
Copy Markdown
Member

Ryan Cavanaugh (@RyanCavanaugh) Not really sure why we'd take this. It generally increases memory by 0.5% and has no positive effect on performance.

@jakebailey
Jake Bailey (jakebailey) marked this pull request as draft October 30, 2026 14:44

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Author: Team Domain: Performance Reports of unusually slow behavior For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Needs merge

Development

Successfully merging this pull request may close these issues.

5 participants