Skip to content

Skip assignments when narrowing identifiers - #64878

Open
Jake Bailey (jakebailey) wants to merge 2 commits into
microsoft:mainfrom
jakebailey:perf/flow-skip-assignments
Open

Jake Bailey (jakebailey) wants to merge 2 commits into
microsoft:mainfrom
jakebailey:perf/flow-skip-assignments

Conversation

@jakebailey

Copy link
Copy Markdown
Member

See the two commits for more info.

Narrowing an identifier walks back through every assignment on its
flow path. In long straight-line functions each reference walks past
every earlier declaration, so the walk length grows with the function
and most of the time goes to cache misses loading each declaration
and its name, only to find a different name.

The binder now records a hash of the identifier each assignment flow
node can target, in what was padding in FlowNode. Walks for an
identifier skip assignments whose recorded name differs without
touching their syntax. Assignments that could matter for any
reference, like for-in variables, are recorded as unknown and still
checked in full.

--checkers 4, n=8:

                  │  before   │             after               │
                  │   sec/op   │   sec/op    vs base              │
Tsc/bluesky         2.794 ± 3%   2.757 ± 2%       ~ (p=0.195 n=8)
Tsc/sentry          11.02 ± 2%   11.17 ± 3%       ~ (p=0.505 n=8)
Tsc/t3code-server   13.10 ± 2%   12.04 ± 2%  -8.14% (p=0.000 n=8)
geomean             7.388        7.184       -2.77%

                  │   before    │              after                │
                  │    rss-B     │    rss-B      vs base              │
Tsc/bluesky         1.193Gi ± 2%   1.191Gi ± 1%       ~ (p=0.574 n=8)
Tsc/sentry          3.357Gi ± 4%   3.626Gi ± 8%       ~ (p=0.105 n=8)
Tsc/t3code-server   2.888Gi ± 3%   2.855Gi ± 3%  -1.16% (p=0.038 n=8)
geomean             2.262Gi        2.310Gi       +2.14%

                  │   before    │              after                │
                  │    heap-B    │    heap-B     vs base              │
Tsc/bluesky         934.7Mi ± 0%   934.8Mi ± 0%       ~ (p=0.645 n=8)
Tsc/sentry          2.609Gi ± 0%   2.608Gi ± 0%       ~ (p=0.130 n=8)
Tsc/t3code-server   2.302Gi ± 0%   2.303Gi ± 0%       ~ (p=0.878 n=8)
geomean             1.763Gi        1.763Gi       -0.02%
Narrowing a property access like x.y walks the same long assignment
chains as narrowing an identifier, and was still visiting the syntax
of every assignment on the way. Such a reference can only be affected
by assignments to x itself or to access chains rooted at x.

Record the root identifier of access chain assignment targets as well,
marked so that identifier references still ignore them, and let walks
for access chains rooted at an identifier skip assignments rooted at
any other name.

--checkers 4, n=6:

                  │  before   │             after               │
                  │   sec/op   │   sec/op    vs base              │
Tsc/bluesky         2.750 ± 6%   2.814 ± 4%       ~ (p=0.084 n=6)
Tsc/sentry          11.28 ± 2%   11.34 ± 2%       ~ (p=0.818 n=6)
Tsc/t3code-server   12.19 ± 9%   11.01 ± 4%  -9.71% (p=0.002 n=6)
Tsc/vscode          12.93 ± 5%   12.90 ± 1%       ~ (p=0.589 n=6)
geomean             8.363        8.203       -1.91%

                  │   before    │              after                │
                  │    rss-B     │    rss-B      vs base              │
Tsc/bluesky         1.197Gi ± 2%   1.200Gi ± 2%       ~ (p=0.818 n=6)
Tsc/sentry          3.527Gi ± 5%   3.512Gi ± 5%       ~ (p=0.937 n=6)
Tsc/t3code-server   2.859Gi ± 3%   2.840Gi ± 2%       ~ (p=0.818 n=6)
Tsc/vscode          4.826Gi ± 3%   4.969Gi ± 3%  +2.96% (p=0.026 n=6)
geomean             2.763Gi        2.777Gi       +0.51%

                  │   before    │              after                │
                  │    heap-B    │    heap-B     vs base              │
Tsc/bluesky         934.7Mi ± 0%   934.7Mi ± 0%       ~ (p=0.937 n=6)
Tsc/sentry          2.607Gi ± 0%   2.608Gi ± 0%       ~ (p=0.937 n=6)
Tsc/t3code-server   2.303Gi ± 0%   2.303Gi ± 0%       ~ (p=0.589 n=6)
Tsc/vscode          4.104Gi ± 0%   4.104Gi ± 0%       ~ (p=0.589 n=6)
geomean             2.178Gi        2.178Gi       +0.01%
Copilot AI balanced review requested due to automatic review settings October 19, 2026 20:44
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Oct 19, 2026
@typescript-automation typescript-automation Bot added Author: Team For Uncommitted Bug PR for untriaged, rejected, closed or missing bug labels Oct 19, 2026
@jakebailey

Copy link
Copy Markdown
Member Author

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

@typescript-automation

typescript-automation Bot commented Oct 19, 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

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.

🔵 Needs a closer look

The optimization bypasses core narrowing checks, and compiler-suite validation was unavailable to establish behavioral equivalence.

No open issues 🎉

What changed in this PR

Optimizes control-flow narrowing by skipping assignments that cannot affect the reference being checked.

Changes:

  • Adds hashed assignment-target metadata to flow nodes.
  • Populates that metadata during binding.
  • Uses reference-root hashes to skip unrelated assignments during narrowing.
File Description
tsc/​internal/​checker/​flow.go Skips assignments using reference-root hashes.
tsc/​internal/​binder/​binder.go Records assignment-target metadata during binding.
tsc/​internal/​ast/​flow.go Defines target metadata and hashing helpers.

Review effort: Balanced

@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=12
Symbols 115,276 115,276 ~ ~ ~ p=1.000 n=12
Types 79,613 79,613 ~ ~ ~ p=1.000 n=12
Memory Used 113,497k (± 0.08%) 113,598k (± 0.07%) ~ 113,418k 113,801k p=0.076 n=12
Memory Allocs 2,212,523 (± 0.00%) 2,212,615 (± 0.01%) ~ 2,212,348 2,212,959 p=0.514 n=12
Config Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.040s (± 6.11%) 0.039s (± 2.95%) ~ 0.036s 0.042s p=0.742 n=12
Bind Time 0.014s (± 8.84%) 0.013s (± 7.80%) ~ 0.011s 0.016s p=0.127 n=12
Check Time 0.471s (± 1.08%) 0.466s (± 0.84%) ~ 0.457s 0.479s p=0.138 n=12
Emit Time 0.264s (± 1.75%) 0.266s (± 2.64%) ~ 0.253s 0.291s p=0.831 n=12
Total Time 0.796s (± 0.74%) 0.791s (± 1.09%) ~ 0.777s 0.822s p=0.213 n=12
angular-1 - native
Errors 3 3 ~ ~ ~ p=1.000 n=12
Symbols 846,592 (± 0.10%) 847,798 (± 0.06%) +1,206 (+ 0.14%) 846,418 849,017 p=0.028 n=12
Types 247,031 (± 0.00%) 247,029 (± 0.00%) -3 (- 0.00%) 247,027 247,031 p=0.000 n=12
Memory Used 596,378k (± 0.03%) 596,451k (± 0.02%) ~ 596,192k 596,815k p=0.600 n=12
Memory Allocs 13,147,168 (± 0.08%) 13,142,798 (± 0.05%) ~ 13,136,642 13,168,608 p=0.514 n=12
Config Time 0.017s (± 1.86%) 0.017s (± 1.95%) ~ 0.016s 0.017s p=1.000 n=12
Parse Time 0.230s (± 3.10%) 0.224s (± 2.83%) ~ 0.211s 0.242s p=0.284 n=12
Bind Time 0.077s (±16.88%) 0.081s (±15.93%) ~ 0.058s 0.105s p=0.660 n=12
Check Time 0s 0s ~ ~ ~ p=1.000 n=12
Emit Time 1.556s (± 1.22%) 1.552s (± 1.38%) ~ 1.498s 1.604s p=0.788 n=12
Total Time 1.897s (± 1.09%) 1.891s (± 1.21%) ~ 1.813s 1.944s p=0.681 n=12
arktype - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,757,463 1,757,463 ~ ~ ~ p=1.000 n=12
Types 943,966 943,966 ~ ~ ~ p=1.000 n=12
Memory Used 703,778k (± 0.01%) 703,616k (± 0.01%) -162k (- 0.02%) 703,449k 703,793k p=0.004 n=12
Memory Allocs 13,290,981 (± 0.01%) 13,294,422 (± 0.02%) +3,441 (+ 0.03%) 13,289,888 13,302,985 p=0.024 n=12
Config Time 0.002s 0.002s ~ ~ ~ p=1.000 n=12
Parse Time 0.068s (± 3.29%) 0.072s (± 4.45%) ~ 0.065s 0.080s p=0.137 n=12
Bind Time 0.022s (± 9.82%) 0.023s (±15.46%) ~ 0.018s 0.033s p=0.920 n=12
Check Time 2.063s (± 0.31%) 2.064s (± 0.50%) ~ 2.036s 2.095s p=0.898 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 2.197s (± 0.25%) 2.205s (± 0.46%) ~ 2.177s 2.239s p=0.183 n=12
mui-docs - native
Errors 11,405 11,405 ~ ~ ~ p=1.000 n=12
Symbols 4,331,983 4,331,983 ~ ~ ~ p=1.000 n=12
Types 1,370,520 1,370,520 ~ ~ ~ p=1.000 n=12
Memory Used 2,625,451k (± 0.07%) 2,625,969k (± 0.05%) ~ 2,622,061k 2,628,661k p=0.630 n=12
Memory Allocs 27,708,378 (± 0.08%) 27,704,382 (± 0.07%) ~ 27,663,046 27,753,144 p=0.755 n=12
Config Time 0.017s (± 1.45%) 0.017s (± 1.07%) ~ 0.016s 0.017s p=1.000 n=12
Parse Time 0.478s (± 1.67%) 0.477s (± 2.44%) ~ 0.448s 0.513s p=0.670 n=12
Bind Time 0.002s 0.002s ~ ~ ~ p=1.000 n=12
Check Time 7.284s (± 0.48%) 7.186s (± 0.76%) -0.098s (- 1.34%) 7.110s 7.366s p=0.007 n=12
Emit Time 0.499s (±11.85%) 0.520s (±13.33%) ~ 0.413s 0.659s p=0.966 n=12
Total Time 9.020s (± 0.51%) 8.946s (± 0.75%) -0.074s (- 0.83%) 8.733s 9.054s p=0.037 n=12
sentry - native
Errors 2 2 ~ ~ ~ p=1.000 n=12
Symbols 7,459,653 7,459,653 ~ ~ ~ p=1.000 n=12
Types 2,647,172 2,647,109 -63 (- 0.00%) ~ ~ p=0.000 n=12
Memory Used 3,804,102k (± 0.04%) 3,804,595k (± 0.04%) ~ 3,801,454k 3,809,411k p=0.671 n=12
Memory Allocs 77,188,195 (± 0.08%) 77,172,538 (± 0.06%) ~ 77,020,583 77,299,229 p=0.630 n=12
Config Time 0.060s (± 0.54%) 0.060s (± 0.54%) ~ 0.060s 0.061s p=1.000 n=12
Parse Time 1.048s (± 3.13%) 1.008s (± 3.08%) ~ 0.954s 1.102s p=0.068 n=12
Bind Time 0.296s (±12.09%) 0.361s (±14.21%) 🔻+0.066s (+22.20%) 0.264s 0.456s p=0.034 n=12
Check Time 14.888s (± 0.62%) 14.688s (± 0.45%) -0.200s (- 1.34%) 14.574s 14.936s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 16.363s (± 0.60%) 16.194s (± 0.38%) -0.169s (- 1.03%) 16.062s 16.429s p=0.001 n=12
social-app - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 2,568,629 2,568,629 ~ ~ ~ p=1.000 n=12
Types 1,163,035 1,163,035 ~ ~ ~ p=1.000 n=12
Memory Used 1,213,025k (± 0.01%) 1,212,901k (± 0.02%) ~ 1,212,189k 1,213,549k p=0.369 n=12
Memory Allocs 14,008,484 (± 0.18%) 13,986,422 (± 0.18%) ~ 13,935,840 14,041,915 p=0.242 n=12
Config Time 0.017s 0.017s (± 1.86%) +0.000s (+ 2.45%) 0.017s 0.018s p=0.037 n=12
Parse Time 0.358s (± 1.09%) 0.355s (± 1.59%) ~ 0.338s 0.366s p=0.600 n=12
Bind Time 0.076s (± 4.35%) 0.084s (±15.21%) ~ 0.069s 0.133s p=0.701 n=12
Check Time 3.655s (± 0.24%) 3.597s (± 0.34%) -0.058s (- 1.58%) 3.557s 3.628s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 4.128s (± 0.21%) 4.075s (± 0.36%) -0.052s (- 1.27%) 4.022s 4.107s p=0.000 n=12
strada-build-src - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,388,680 1,388,680 ~ ~ ~ p=1.000 n=12
Types 441,862 441,858 -4 (- 0.00%) ~ ~ p=0.000 n=12
Memory Used 1,316,830k (± 0.62%) 1,314,476k (± 0.74%) ~ 1,296,961k 1,347,353k p=0.410 n=12
Memory Allocs 78,248,297 (± 0.07%) 78,212,454 (± 0.13%) ~ 77,815,183 78,369,466 p=0.932 n=12
Config Time 0.005s (± 9.18%) 0.005s (±10.49%) ~ 0.003s 0.006s p=0.677 n=12
Parse Time 0.313s (± 2.52%) 0.305s (± 1.20%) -0.008s (- 2.51%) 0.296s 0.316s p=0.032 n=12
Bind Time 0.000s (±217.90%) 0.000s ~ ~ ~ p=1.000 n=12
Check Time 2.001s (± 0.35%) 1.971s (± 0.68%) -0.030s (- 1.50%) 1.941s 2.011s p=0.000 n=12
Emit Time 0.361s (± 2.60%) 0.367s (± 5.85%) ~ 0.321s 0.418s p=0.966 n=12
Total Time 23.883s (± 0.40%) 23.601s (± 0.52%) -0.282s (- 1.18%) 23.177s 23.947s p=0.001 n=12
strada-compiler - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 334,953 334,953 ~ ~ ~ p=1.000 n=12
Types 197,616 197,616 ~ ~ ~ p=1.000 n=12
Memory Used 247,502k (± 0.02%) 247,546k (± 0.02%) ~ 247,425k 247,715k p=0.143 n=12
Memory Allocs 4,329,613 (± 0.01%) 4,329,958 (± 0.02%) ~ 4,328,496 4,334,301 p=0.755 n=12
Config Time 0.001s 0.001s ~ ~ ~ p=1.000 n=12
Parse Time 0.108s (± 4.70%) 0.112s (± 4.78%) ~ 0.101s 0.129s p=0.417 n=12
Bind Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Check Time 0.961s (± 0.69%) 0.944s (± 0.60%) -0.017s (- 1.73%) 0.927s 0.957s p=0.000 n=12
Emit Time 0.147s (± 4.31%) 0.147s (± 4.66%) ~ 0.124s 0.162s p=0.988 n=12
Total Time 1.271s (± 0.87%) 1.253s (± 0.79%) -0.018s (- 1.42%) 1.231s 1.282s p=0.023 n=12
t3code-server - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 11,588,087 11,588,087 ~ ~ ~ p=1.000 n=12
Types 3,062,556 3,062,556 ~ ~ ~ p=1.000 n=12
Memory Used 3,946,195k (± 0.01%) 3,947,544k (± 0.01%) +1,349k (+ 0.03%) 3,945,776k 3,948,795k p=0.002 n=12
Memory Allocs 41,536,723 (± 0.05%) 41,551,587 (± 0.05%) ~ 41,504,511 41,616,359 p=0.378 n=12
Config Time 0.006s 0.006s ~ ~ ~ p=1.000 n=12
Parse Time 0.563s (± 3.40%) 0.571s (± 1.88%) ~ 0.545s 0.598s p=0.599 n=12
Bind Time 0.203s (±13.87%) 0.169s (± 5.28%) ~ 0.153s 0.190s p=0.203 n=12
Check Time 18.571s (± 0.26%) 14.996s (± 0.50%) 🟩-3.574s (-19.25%) 14.803s 15.148s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 19.363s (± 0.24%) 15.760s (± 0.41%) 🟩-3.603s (-18.61%) 15.586s 15.899s p=0.000 n=12
ts-pre-modules - native
Errors 87 87 ~ ~ ~ p=1.000 n=12
Symbols 303,834 303,834 ~ ~ ~ p=1.000 n=12
Types 181,663 181,663 ~ ~ ~ p=1.000 n=12
Memory Used 215,247k (± 0.02%) 215,271k (± 0.02%) ~ 215,147k 215,447k p=0.504 n=12
Memory Allocs 1,401,476 (± 0.02%) 1,401,615 (± 0.02%) ~ 1,400,829 1,402,523 p=0.418 n=12
Config Time 0.000s (±217.90%) 0.000s ~ ~ ~ p=1.000 n=12
Parse Time 0.094s (± 3.01%) 0.098s (± 4.22%) ~ 0.085s 0.108s p=0.102 n=12
Bind Time 0.040s (± 9.14%) 0.040s (± 6.79%) ~ 0.031s 0.045s p=0.921 n=12
Check Time 0.744s (± 0.98%) 0.735s (± 0.81%) ~ 0.720s 0.748s p=0.182 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 0.893s (± 0.74%) 0.889s (± 0.72%) ~ 0.875s 0.906s p=0.417 n=12
vscode - native
Errors 396 396 ~ ~ ~ p=1.000 n=12
Symbols 12,424,509 12,424,509 ~ ~ ~ p=1.000 n=12
Types 4,049,620 4,049,608 -12 (- 0.00%) ~ ~ p=0.000 n=12
Memory Used 5,828,907k (± 0.02%) 5,828,105k (± 0.01%) ~ 5,825,917k 5,829,732k p=0.291 n=12
Memory Allocs 59,231,297 (± 0.02%) 59,246,603 (± 0.06%) ~ 59,215,702 59,372,256 p=0.887 n=12
Config Time 0.062s (± 0.46%) 0.062s ~ ~ ~ p=0.217 n=12
Parse Time 1.468s (± 2.57%) 1.485s (± 2.34%) ~ 1.392s 1.577s p=0.443 n=12
Bind Time 0.549s (±15.60%) 0.513s (±14.80%) ~ 0.412s 0.753s p=0.682 n=12
Check Time 12.183s (± 0.75%) 12.077s (± 0.78%) ~ 11.811s 12.210s p=0.143 n=12
Emit Time 4.745s (± 6.04%) 4.482s (± 8.64%) ~ 3.811s 5.378s p=0.266 n=12
Total Time 19.082s (± 1.30%) 18.692s (± 1.85%) -0.390s (- 2.04%) 18.089s 19.553s p=0.045 n=12
webpack - native
Errors 598 598 ~ ~ ~ p=1.000 n=12
Symbols 1,343,495 1,343,495 ~ ~ ~ p=1.000 n=12
Types 651,492 651,492 ~ ~ ~ p=1.000 n=12
Memory Used 855,378k (± 0.02%) 855,377k (± 0.01%) ~ 855,093k 855,569k p=0.932 n=12
Memory Allocs 6,770,284 (± 0.02%) 6,768,513 (± 0.01%) ~ 6,766,778 6,770,158 p=0.101 n=12
Config Time 0.009s 0.009s ~ ~ ~ p=1.000 n=12
Parse Time 0.310s (± 3.04%) 0.302s (± 3.34%) ~ 0.267s 0.322s p=0.468 n=12
Bind Time 0.079s (±13.47%) 0.079s (± 6.74%) ~ 0.068s 0.097s p=0.323 n=12
Check Time 2.791s (± 0.60%) 2.659s (± 0.31%) 🟩-0.132s (- 4.72%) 2.638s 2.676s p=0.000 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 3.228s (± 0.47%) 3.088s (± 0.34%) 🟩-0.140s (- 4.33%) 3.048s 3.112s p=0.000 n=12
xstate-main - native
Errors 0 0 ~ ~ ~ p=1.000 n=12
Symbols 1,060,810 1,060,810 ~ ~ ~ p=1.000 n=12
Types 389,671 389,671 ~ ~ ~ p=1.000 n=12
Memory Used 471,991k (± 0.03%) 471,843k (± 0.02%) ~ 471,685k 472,157k p=0.143 n=12
Memory Allocs 4,223,038 (± 0.06%) 4,217,293 (± 0.08%) -5,745 (- 0.14%) 4,206,475 4,225,874 p=0.008 n=12
Config Time 0.003s 0.003s ~ ~ ~ p=1.000 n=12
Parse Time 0.137s (± 2.49%) 0.133s (± 2.85%) ~ 0.127s 0.145s p=0.146 n=12
Bind Time 0.042s (±16.23%) 0.050s (±12.38%) ~ 0.034s 0.065s p=0.085 n=12
Check Time 1.098s (± 1.24%) 1.081s (± 0.87%) ~ 1.066s 1.121s p=0.103 n=12
Emit Time 0.000s 0.000s ~ ~ ~ p=1.000 n=12
Total Time 1.286s (± 1.07%) 1.275s (± 0.97%) ~ 1.243s 1.298s p=0.131 n=12
System info unknown
Hosts
  • native
Scenarios
  • Compiler-Unions - native
  • angular-1 - native
  • arktype - native
  • mui-docs - native
  • sentry - native
  • social-app - native
  • strada-build-src - native
  • strada-compiler - native
  • t3code-server - native
  • ts-pre-modules - native
  • vscode - native
  • webpack - native
  • xstate-main - native
Benchmark Name Iterations
Current pr 12
Baseline baseline 12

Developer Information:

Download Benchmarks

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 For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Not started

Development

Successfully merging this pull request may close these issues.

2 participants