Repository navigation
Various memory optimizations - #64734
Draft
Jake Bailey (jakebailey) wants to merge 41 commits into
Draft
Jake Bailey (jakebailey) wants to merge 41 commits into
Jake Bailey (jakebailey) wants to merge 41 commits into
Conversation
Replace nine nodeData interface methods with generated Kind-switch dispatchers to reduce interface method promotion and itab size.
This reverts commit 9baebce.
Replace large sparse Kind switches with byte-indexed dispatch tables and dense switches, while leaving small switches unchanged. Keep the concrete calls visible to escape analysis without restoring promoted interface methods. Add accessor correctness tests and fixture-based benchmarks covering both mixed nodes and nodes with populated accessor results.
Index generated accessor tables directly with the node kind. Invalid kinds now hit the normal Go bounds check rather than returning nil. Update accessor tests to use valid constructed nodes.
…patch-opt # Conflicts: # tsc/internal/ast/kind_stringer_generated.go
Keep the accessor benchmarks compatible with the rooted path types and filesystem case sensitivity API now used on main.
Each type carries a self-referential interface solely to recover its concrete representation. That costs space in every type and adds GC pointer work even though the representation is fixed at construction. A compact concrete-kind tag saves 16 bytes per type on 64-bit hosts. Keep it separate from mutable semantic flags so casts stay valid as checking discovers more information.
Dense checker link stores still keep a pointer for every occupied slot. Those tables cost memory and require GC scanning even though values already live in arenas. Use four-byte indexes in the page tables. Retain bounded arena blocks so references stay stable without geometrically over-allocating large stores. Keep map-backed stores unchanged: compressing those did not improve allocation usage.
Common type representations create many small heap objects during checking, adding allocator and GC bookkeeping costs. Use the existing bounded typed arenas for frequently allocated types. Unlike an arena that owns the entire graph, old blocks can still be reclaimed when no live type references them.
Large type lists repeatedly grow temporary hash buffers even though
their encoded length is already known. These discarded allocations
account for a substantial part of checker allocation traffic.
Reserve enough capacity before writing each list, retaining the small
inline path and the exact existing hash byte stream. Allocation tests
cover the inline boundary and large lists, and a fresh-program benchmark
includes parsing and binding rather than relying only on warmed ASTs.
CPU wall timings are noisy on this machine; report bytes and allocation
counts instead.
goos: linux
goarch: amd64
pkg: github.com/microsoft/TypeScript/tsc/internal/checker
cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz
│ key-before.txt │ key-reserve.txt │
│ B/op │ B/op vs base │
TypeListKey/8-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
TypeListKey/46-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
TypeListKey/47-20 576.0 ± 0% 208.0 ± 0% -63.89% (p=0.002 n=6)
TypeListKey/256-20 2.375Ki ± 0% 1.125Ki ± 0% -52.63% (p=0.002 n=6)
TypeListKey/4096-20 81.25Ki ± 0% 18.00Ki ± 0% -77.85% (p=0.002 n=6)
geomean ² -48.03% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
│ key-before.txt │ key-reserve.txt │
│ allocs/op │ allocs/op vs base │
TypeListKey/8-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
TypeListKey/46-20 0.000 ± 0% 0.000 ± 0% ~ (p=1.000 n=6) ¹
TypeListKey/47-20 2.000 ± 0% 1.000 ± 0% -50.00% (p=0.002 n=6)
TypeListKey/256-20 4.000 ± 0% 1.000 ± 0% -75.00% (p=0.002 n=6)
TypeListKey/4096-20 13.000 ± 0% 1.000 ± 0% -92.31% (p=0.002 n=6)
geomean ² -60.50% ²
¹ all samples are equal
² summaries must be >0 to compute geomean
A presence query needs only the page entry, not its value. Resolving an occupied index adds arena lookup work to a boolean check. Keep presence checks on the compressed page table, and apply the repository linter fixes to the link-store tests.
The linter rewrites integer formatting calls without updating their imports. Keep the corrected test code buildable with strconv.
Union and intersection normalization creates temporary lists even when
an equivalent type already exists. Mapping and narrowing repeat this work
throughout checking, producing unnecessary garbage. Reentrant, bounded
workspaces let canonicalization reuse that storage while published types
and subtype-reduction caches retain independently owned constituents.
Fresh parse, bind, and check of the compiler fixture (six samples):
│ before │ after │
│ B/op │ B/op vs base │
CheckFreshProgram-20 303.0Mi 277.2Mi -8.51% (p=0.002 n=6)
│ before │ after │
│ allocs/op │ allocs/op vs base │
CheckFreshProgram-20 1.393M 1.139M -18.26% (p=0.002 n=6)
VS Code src: identical diagnostics and compiler counts, 31.01M to 28.52M
allocations. One fixed-work instruction sample fell from 344.57B to
324.08B; timing is noisy and not evidence of a reliable wall-time gain.
Retained memory was effectively unchanged at 5.38 GiB.
npx hereby validate passed; focused ownership tests also passed with race
and checkptr instrumentation.
Every mapper currently carries a self-referential interface, and each
inference context separately allocates two mappers with the same lifetime.
A concrete kind header eliminates the interface and lets inference
contexts own their fixing and non-fixing mappers in one allocation.
Fresh parse, bind, and check versus reusable normalization workspaces:
│ before │ after │
│ B/op │ B/op vs base │
CheckFreshProgram-20 277.2Mi 276.0Mi -0.44% (p=0.002 n=6)
│ before │ after │
│ allocs/op │ allocs/op vs base │
CheckFreshProgram-20 1.139M 1.094M -3.90% (p=0.002 n=6)
VS Code allocations decrease from 28.52M to 27.17M with identical
compiler counts and diagnostics. Retained memory is effectively unchanged.
Three interleaved fixture runs show fewer fixed-work instructions, but a
single VS Code run is noisy; no wall-time claim is made.
npx hereby validate and focused race/checkptr tests passed.
Most value symbols never need write types, computed names, or containing
types. These three pointers nevertheless occupy every checker link record,
which is the largest retained allocation on the VS Code workload.
Allocate that metadata only when it is used, shrinking the hot record
from 56 to 40 bytes without changing symbol identity or cache semantics.
VS Code src, six samples versus compact inference mappers:
│ before │ after │
│ retained-B │ retained-B vs base │
ExternalProgram-20 5.029Gi 4.874Gi -3.09% (p=0.002 n=6)
│ before │ after │
│ scan-B │ scan-B vs base │
ExternalProgram-20 4.664Gi 4.508Gi -3.33% (p=0.002 n=6)
│ before │ after │
│ B/op │ B/op vs base │
ExternalProgram-20 6.200Gi 6.043Gi -2.54% (p=0.002 n=6)
Allocation count is unchanged. All 321 semantic diagnostics and all
10284 files are identical. CLI diagnostic text and compiler counts match
exactly as well. No GC tuning is used.
npx hereby validate and focused race/checkptr ownership tests passed.
Instantiated and inherited object types retain a name-to-symbol map beside
their ordered property list. Those maps duplicate symbol pointers and
string headers across millions of types, increasing both live heap and
the amount of memory the collector must scan.
Use the existing property list as the source of truth for immutable named
members. Small types need no separate index; larger types need only
pointer-free integer slots. Keep map semantics for mutable module exports,
reserved members, mismatched names, and recursive member resolution.
VS Code src, six samples versus cold value-symbol links:
goos: linux
goarch: amd64
pkg: github.com/microsoft/TypeScript/tsc/internal/checker
cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz
│ vscode-cold-links.txt │ vscode-member-index-owned.txt │
│ retained-B │ retained-B vs base │
ExternalProgram-20 4.874Gi ± 0% 4.503Gi ± 0% -7.61% (p=0.002 n=6)
│ vscode-cold-links.txt │ vscode-member-index-owned.txt │
│ scan-B │ scan-B vs base │
ExternalProgram-20 4.508Gi ± 0% 4.062Gi ± 0% -9.89% (p=0.002 n=6)
│ vscode-cold-links.txt │ vscode-member-index-owned.txt │
│ B/op │ B/op vs base │
ExternalProgram-20 6.043Gi ± 0% 5.982Gi ± 0% -1.00% (p=0.002 n=6)
│ vscode-cold-links.txt │ vscode-member-index-owned.txt │
│ allocs/op │ allocs/op vs base │
ExternalProgram-20 26.23M ± 0% 26.04M ± 0% -0.71% (p=0.002 n=6)
All 321 semantic diagnostics and 10284 files are identical. CLI diagnostic
text and compiler counts match exactly. Instruction measurements remain
noisy; this change does not claim an established CPU improvement.
No GC tuning is used.
npx hereby validate and focused race/checkptr tests passed. Existing
compiler and editor baselines are unchanged, including recursive CommonJS
member resolution.
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Bring in the existing nodeData dispatch work before changing AST storage. Keep subsequent representation changes in separate normal commits.
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Every node retains a self-referential interface even though its concrete allocation embeds the node header and its kind already selects dispatch. Across the VS Code source tree, that redundant metadata keeps hundreds of megabytes live and exposes another pointer per node to the collector. Remove the redundant reference without compressing parent or child pointers. The node header shrinks from 48 to 32 bytes on amd64, while generated casts check the concrete kind and enforce offset-zero embedding at compile time. Direct pointers preserve cheap navigation and do not introduce factory-wide ownership or cross-factory lookup tables. Baseline: 70510c0, which already includes the existing generated nodeData accessor work and all preceding checker optimizations. These numbers do not attribute the accessor foundation's gains to this change. VS Code src, six interleaved fresh-process samples: goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-direct-node-foundation.txt │ vscode-direct-node.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 4.504Gi ± 0% 4.180Gi ± 0% -7.18% (p=0.002 n=6) │ vscode-direct-node-foundation.txt │ vscode-direct-node.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 4.063Gi ± 0% 3.738Gi ± 0% -8.00% (p=0.002 n=6) │ vscode-direct-node-foundation.txt │ vscode-direct-node.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.983Gi ± 0% 5.659Gi ± 0% -5.41% (p=0.002 n=6) │ vscode-direct-node-foundation.txt │ vscode-direct-node.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 26.04M ± 0% 26.05M ± 0% ~ (p=0.589 n=6) │ direct-node-foundation-instructions.txt │ direct-node-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalProgram-20 315.0G ± 5% 302.7G ± 4% ~ (p=0.132 n=6) Instruction counts are statistically inconclusive on this noisy machine. All 321 semantic diagnostics and 10284 files are unchanged. CLI diagnostic text and symbol, type, and instantiation counts match exactly. No GC settings are changed. npx hereby validate --all passed. Compiler and editor baselines remain unchanged. Focused race/checkptr checks cover layout, wrong-kind casts, canonical token representations, shared kinds, cloning, and GC reachability.
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Most symbols do not need members, exports, or an export-symbol link. Keeping all three pointers in every symbol's shared metadata charges every property, parameter, and synthetic symbol for those features. Move them into optional shared metadata, reducing SymbolWithData from 104 to 88 bytes on amd64. Binder- and checker-owned typed arenas batch the sidecars without adding an allocator pointer to every symbol. Shared symbolData still sees sidecars created through either alias. Compared with 823a64b, which already includes the checker storage and direct-header AST changes. Six fresh-process interleaved samples parse, bind, and check ../vscode/src with the same single checker and normal GC settings. Both workloads report 321 diagnostics, 10284 files. Exact CLI diagnostics and compiler counts also match. CPU timings are noisy. Hardware instructions and allocation counts show no statistically established difference; do not claim CPU gains. Validation: npx hereby validate --all passed. Focused symbol tests also passed with -race and -gcflags=all=-d=checkptr=2. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-symbol-batched-before.txt │ vscode-symbol-batched.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 4.180Gi ± 0% 4.129Gi ± 0% -1.21% (p=0.002 n=6) │ vscode-symbol-batched-before.txt │ vscode-symbol-batched.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.738Gi ± 0% 3.687Gi ± 0% -1.34% (p=0.002 n=6) │ vscode-symbol-batched-before.txt │ vscode-symbol-batched.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.659Gi ± 2% 5.606Gi ± 0% -0.93% (p=0.002 n=6) │ vscode-symbol-batched-before.txt │ vscode-symbol-batched.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 26.06M ± 2% 26.08M ± 0% ~ (p=0.310 n=6) │ symbol-batched-before-instructions.txt │ symbol-batched-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 302.1G ± 4% 299.9G ± 3% ~ (p=0.589 n=6)
Avoiding unused instantiated members complements cheaper retained symbols and indexes, but both approaches eliminate some of the same storage. Measure their combined effect rather than adding percentages from independent upstream comparisons. Layer local unified-member-resolution at d52b33f onto 7990e68. Partial resolution keeps its selective cache. Fully enumerated types publish compact member indexes, preserving provisional visibility for recursive filtering and live module tables. Adapt setter checks to the cold value-symbol link accessor. Extend identity coverage beyond the small-table linear-scan threshold. Six fresh-process interleaved samples parse, bind, and check the actual VS Code src/tsconfig.json with one checker and normal GC settings. The baseline already includes the checker, AST, and symbol reductions. Live heap falls another 4.16%, GC scan bytes fall 3.65%, and checker symbol creation falls 34.94%. Allocated bytes fall only 0.53%, while allocation events rise 4.88%; this remains a real tradeoff. Neither elapsed time nor hardware instructions differs significantly. Both variants report 321 diagnostics and 10284 files. The production CLI diagnostic text is byte-identical, including its expected exit 2. Validation passed: npx hereby test npx hereby validate --all go -C tsc test -race -gcflags=all=-d=checkptr=2 ./internal/checker -run 'TestInstantiated|TestMemberIndex' -count=1 Task-only measurement helpers are not included. Exact benchstat output: goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ sec/op │ sec/op vs base │ ExternalProgram-20 38.32 ± 9% 38.76 ± 8% ~ (p=0.699 n=6) │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ diagnostics │ diagnostics vs base │ ExternalProgram-20 321.0 ± 0% 321.0 ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ files │ files vs base │ ExternalProgram-20 10.28k ± 0% 10.28k ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 4.130Gi ± 0% 3.958Gi ± 0% -4.16% (p=0.002 n=6) │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.688Gi ± 0% 3.553Gi ± 0% -3.65% (p=0.002 n=6) │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ symbols │ symbols vs base │ ExternalProgram-20 6.385M ± 0% 4.154M ± 0% -34.94% (p=0.002 n=6) │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.606Gi ± 0% 5.576Gi ± 0% -0.53% (p=0.002 n=6) │ vscode-unified-on-compact-before.txt │ vscode-unified-on-compact.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 26.07M ± 0% 27.35M ± 0% +4.88% (p=0.002 n=6) │ unified-on-compact-before-instructions.txt │ unified-on-compact-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 290.9G ± 5% 301.4G ± 5% ~ (p=0.589 n=6)
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Every object, union, and intersection paid for signature and index-info slice headers, a call-signature count, and a node-builder cache, even when none were needed. Those fields made StructuredType 144 bytes on amd64. Keep them in an optional checker-arena sidecar and reduce the hot record to 88 bytes. Preserve signature publication during recursive anonymous resolution, clipped public signature slices, index-info nil/empty distinctions, and independent node-builder cache identity. Empty unresolved shape does not allocate a sidecar. Compared with 1ef0b40, including unified member resolution and all previous checker, AST, and symbol reductions. Six fresh-process interleaved samples parse, bind, and check the actual VS Code project using one checker and normal GC settings. Live heap falls another 1.81%, GC scan bytes fall 2.01%, and allocated bytes fall 1.28%. Allocation counts do not differ significantly. Neither hardware instructions nor elapsed time establishes a change; this does not claim a CPU improvement. Exact CLI diagnostic text and compiler counts match the baseline. Validation passed: npx hereby validate --all, plus focused checker tests with -race and -gcflags=all=-d=checkptr=2. Exact benchstat output: goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-shape-before.txt │ vscode-shape.txt │ │ sec/op │ sec/op vs base │ ExternalProgram-20 41.55 ± 6% 41.84 ± 13% ~ (p=0.937 n=6) │ vscode-shape-before.txt │ vscode-shape.txt │ │ diagnostics │ diagnostics vs base │ ExternalProgram-20 321.0 ± 0% 321.0 ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-shape-before.txt │ vscode-shape.txt │ │ files │ files vs base │ ExternalProgram-20 10.28k ± 0% 10.28k ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-shape-before.txt │ vscode-shape.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.959Gi ± 0% 3.887Gi ± 0% -1.81% (p=0.002 n=6) │ vscode-shape-before.txt │ vscode-shape.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.553Gi ± 0% 3.482Gi ± 0% -2.01% (p=0.002 n=6) │ vscode-shape-before.txt │ vscode-shape.txt │ │ symbols │ symbols vs base │ ExternalProgram-20 4.154M ± 0% 4.154M ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-shape-before.txt │ vscode-shape.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.577Gi ± 0% 5.506Gi ± 0% -1.28% (p=0.002 n=6) │ vscode-shape-before.txt │ vscode-shape.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 27.36M ± 0% 27.35M ± 0% ~ (p=0.818 n=6) │ shape-before-instructions.txt │ shape-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 292.5G ± 4% 301.6G ± 4% ~ (p=0.093 n=6)
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
One function-or-constructor check flag padded every value-symbol link to 40 bytes on amd64, including links for ordinary properties and parameters. Store it in the existing optional metadata instead, making the hot record 32 bytes. The guard is still published before checking the symbol so recursive and repeated checks retain their behavior. Compared with da5b50e, which includes all preceding checker, AST, symbol, unified-member, and structured-shape changes. Six fresh-process interleaved samples parse, bind, and check the actual VS Code project using one checker and normal GC settings. Live heap falls another 0.61%, GC scan bytes fall 0.68%, and allocated bytes fall 0.45%. Allocation events, elapsed time, and instructions have no statistically established difference. Exact production CLI diagnostic text and compiler counts match. Validation passed: npx hereby validate --all, and focused checker tests with -race and -gcflags=all=-d=checkptr=2. Exact benchstat output: goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ sec/op │ sec/op vs base │ ExternalProgram-20 40.92 ± 11% 40.69 ± 7% ~ (p=0.589 n=6) │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ diagnostics │ diagnostics vs base │ ExternalProgram-20 321.0 ± 0% 321.0 ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ files │ files vs base │ ExternalProgram-20 10.28k ± 0% 10.28k ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.887Gi ± 0% 3.864Gi ± 0% -0.61% (p=0.002 n=6) │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.482Gi ± 0% 3.459Gi ± 0% -0.68% (p=0.002 n=6) │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ symbols │ symbols vs base │ ExternalProgram-20 4.154M ± 0% 4.154M ± 0% ~ (p=1.000 n=6) ¹ ¹ all samples are equal │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.506Gi ± 0% 5.481Gi ± 0% -0.45% (p=0.002 n=6) │ vscode-linkflags-before.txt │ vscode-linkflags.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 27.36M ± 0% 27.35M ± 0% ~ (p=0.699 n=6) │ linkflags-before-instructions.txt │ linkflags-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 298.1G ± 3% 301.6G ± 3% ~ (p=0.310 n=6)
Most resolved type nodes never need an anonymous object's outer type parameter list. Reserving a slice header for every node makes the common case needlessly expensive to retain and scan. TypeNodeLinks now occupies 16 rather than 32 bytes on amd64. An optional checker-owned arena entry preserves computed-empty lists and the original slice aliasing without introducing a separate allocation per cached list. VS Code src/tsconfig.json versus 1d11625, six fresh-process interleaved pairs with the normal GC policy. Retained bytes fall 0.43%, scan bytes 0.47%, and allocated bytes 0.33%. Allocation events and instruction counts are not statistically different; no CPU improvement is claimed. Exact production diagnostics and semantic counts match. Compiler, fourslash, unit, focused race/checkptr tests, and hereby validate --all pass. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-typeparams-before.txt │ vscode-typeparams.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.864Gi ± 0% 3.847Gi ± 0% -0.43% (p=0.002 n=6) │ vscode-typeparams-before.txt │ vscode-typeparams.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.459Gi ± 0% 3.443Gi ± 0% -0.47% (p=0.002 n=6) │ vscode-typeparams-before.txt │ vscode-typeparams.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.484Gi ± 0% 5.466Gi ± 0% -0.33% (p=0.002 n=6) │ vscode-typeparams-before.txt │ vscode-typeparams.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 27.38M ± 0% 27.35M ± 0% ~ (p=0.093 n=6) │ typeparams-before-instructions.txt │ typeparams-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 297.2G ± 2% 290.9G ± 4% ~ (p=0.310 n=6)
Most signatures have no type parameters, explicit this parameter, predicate, composite constituents, or isolated-type cache. Reserving this state for every instantiated signature wastes memory and GC scanning. Negative predicate results need no pointer at all. Signature shrinks from 128 to 80 bytes on amd64. Checker-owned cold storage preserves slice aliasing and independent clone caches; a private, non-propagating flag retains the recursive no-predicate guard. A full VS Code field census found 648637 signatures and only 84956 requiring cold storage (13.1%). Six fresh-process interleaved comparisons against efe6e90, with normal GC, save 0.65% retained bytes, 0.73% scan bytes, and 0.49% allocated bytes. Allocation events and instructions are not statistically different. This is incremental, not a CPU breakthrough. Production diagnostics and semantic counts match exactly. Full compiler, fourslash and unit tests, focused race/checkptr tests, and npx hereby validate --all pass. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-signature-before.txt │ vscode-signature.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.848Gi ± 0% 3.823Gi ± 0% -0.65% (p=0.002 n=6) │ vscode-signature-before.txt │ vscode-signature.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.443Gi ± 0% 3.418Gi ± 0% -0.73% (p=0.002 n=6) │ vscode-signature-before.txt │ vscode-signature.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.468Gi ± 0% 5.441Gi ± 0% -0.49% (p=0.002 n=6) │ vscode-signature-before.txt │ vscode-signature.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 27.36M ± 0% 27.36M ± 0% ~ (p=0.589 n=6) │ signature-before-instructions.txt │ signature-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 297.9G ± 3% 296.1G ± 3% ~ (p=0.699 n=6)
A live pointer into an arena chunk retains the entire backing allocation, including unused capacity and unrelated dead values. Resetting typed parser and binder arenas at every file multiplies terminal chunk slack. Uniform 256-object chunks are especially wasteful for large records. Target 512 bytes per chunk before allocator rounding, preserving stable direct pointers and exact-capacity slices. Larger contiguous slices keep their separate allocation path. Indexed arenas retain their index layout. No GC settings, cross-file pooling, or pointer decoding are introduced. Six fresh-process interleaved VS Code comparisons against 69d9821, using normal GC, reduce retained bytes 8.54%, scan bytes 10.44%, allocated bytes 5.74%, and whole-process instructions 9.43%. Allocation events rise 13.91%; wall time is inconclusive on the shared machine (p=0.937). Four-object chunks saved slightly more heap but raised allocation events 32.15%. A 512-byte target keeps most of the memory savings with less than half that allocation penalty. Reusing partial parser and binder chunks also saved memory but would couple otherwise independent file lifetimes. The retention regression fails with the old allocator. Mixed singleton, slice, clone, growth, zero-size, and GC cases preserve addresses and data. Focused race/checkptr checks and npx hereby validate --all pass, including 166152 Go tests and 954 API tests. Production diagnostics and file, line, identifier, symbol, type, and instantiation counts match exactly. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-arena-before.txt │ vscode-arena.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.823Gi ± 0% 3.497Gi ± 0% -8.54% (p=0.002 n=6) │ vscode-arena-before.txt │ vscode-arena.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.418Gi ± 0% 3.062Gi ± 0% -10.44% (p=0.002 n=6) │ vscode-arena-before.txt │ vscode-arena.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.442Gi ± 0% 5.130Gi ± 0% -5.74% (p=0.002 n=6) │ vscode-arena-before.txt │ vscode-arena.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 27.36M ± 0% 31.17M ± 0% +13.91% (p=0.002 n=6) │ arena-before-instructions.txt │ arena-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 300.8G ± 3% 272.5G ± 6% -9.43% (p=0.002 n=6)
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Resolved type nodes and signatures still kept AST pointers as map keys despite already having stable node identities. Those maps add redundant pointer storage, GC scanning, hashing, and growth allocations. Reuse the existing node-link store's pointer-free paged indexes for these two high-volume caches. Ordinary IDs and IDs outside the dense reserved range retain the existing fallback, and caches stay checker-local. Six fresh-process interleaved VS Code comparisons against e97c4aa, using normal GC, reduce retained heap 1.20%, scan bytes 2.12%, allocated bytes 2.11%, and allocation events 0.20%. Instructions are inconclusive. This saves another roughly 43 MiB of heap and 66 MiB of scanning. Production diagnostics and semantic counts match exactly. The full compiler, fourslash, and unit suite passes, as do focused race/checkptr checks for cache identity, ordinary/reserved IDs, clone isolation, growth, GC reachability, and independent checkers. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.497Gi ± 0% 3.455Gi ± 0% -1.20% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 3.061Gi ± 0% 2.997Gi ± 0% -2.12% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.129Gi ± 0% 5.021Gi ± 0% -2.11% (p=0.002 n=6) │ vscode-paged-before-6.txt │ vscode-paged-6.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 31.16M ± 0% 31.09M ± 0% -0.20% (p=0.002 n=6) │ paged-before-instructions.txt │ paged-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 279.8G ± 5% 270.5G ± 2% ~ (p=0.589 n=6)
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ordinary type comparisons need only two 32-bit IDs and intersection context, but retained a 128-bit hash for every cached result. Hashing and storing twice the necessary key width wastes allocations and memory. Keep exact 64-bit pairs in separate maps for the four intersection states. Generic-reference normalization still uses its original full-width hash. An explicit key tag separates both namespaces, including recursion guards, without sacrificing hash bits or changing constrained generic behavior. Six fresh-process interleaved VS Code comparisons against 27786ef, using normal GC, reduce retained heap 0.94% (roughly 33 MiB) and total allocated bytes 1.29%. GC scanning, allocation events, and instruction counts are not statistically different. Tests cover directional and identity comparisons, all intersection contexts, result replacement, exact high-bit IDs, namespace isolation, generic parameter normalization, and constrained versus unconstrained recursion keys. Focused race/checkptr checks and npx hereby validate --all pass, including 166156 Go tests and 954 API tests. Production diagnostics and all semantic counts match exactly. goos: linux goarch: amd64 pkg: github.com/microsoft/TypeScript/tsc/internal/checker cpu: Intel(R) Core(TM) i9-10900K CPU @ 3.70GHz │ vscode-relation-before-6.txt │ vscode-relation-6.txt │ │ retained-B │ retained-B vs base │ ExternalProgram-20 3.456Gi ± 0% 3.424Gi ± 0% -0.94% (p=0.002 n=6) │ vscode-relation-before-6.txt │ vscode-relation-6.txt │ │ scan-B │ scan-B vs base │ ExternalProgram-20 2.998Gi ± 0% 2.997Gi ± 0% ~ (p=0.310 n=6) │ vscode-relation-before-6.txt │ vscode-relation-6.txt │ │ B/op │ B/op vs base │ ExternalProgram-20 5.021Gi ± 0% 4.956Gi ± 0% -1.29% (p=0.002 n=6) │ vscode-relation-before-6.txt │ vscode-relation-6.txt │ │ allocs/op │ allocs/op vs base │ ExternalProgram-20 31.08M ± 0% 31.07M ± 0% ~ (p=0.240 n=6) │ relation-before-instructions.txt │ relation-instructions.txt │ │ instructions/op │ instructions/op vs base │ ExternalInstructions-20 269.3G ± 2% 270.0G ± 3% ~ (p=0.937 n=6)
Member
Author
|
CPU regression appears to be due to #64735 |
Full enumeration was repeating name lookups and base traversal that lazy resolution needs but enumeration has already done. This overhead appeared in the MUI and Webpack regressions after unified resolution. Preserve its selective-lookup symbol and heap savings without paying the same lookup costs while collecting all properties. Six fresh-process interleaved production CLI comparisons against 5f35280, with normal GC and default threading, reduce instructions 2.55% on CI-pinned MUI and 2.17% on Xstate. Webpack and VS Code instruction differences are inconclusive. Allocations decrease on all four workloads. Reported heap is unchanged except for a 0.12% increase on Xstate. Against main aa81492, the combined branch uses 9.74-15.75% fewer instructions and 23.11-31.55% less reported heap across these workloads. These comparisons include all earlier optimizations on the branch. MUI uses source and dependencies from the benchmark's CI-pinned commit 6780195595c12252a130aa5ae5c5bd461229c6b7. Webpack is isolated at 2289141381f2c1c965cb65118fa0ffe2e9e19834 with scenario dependencies restored. VS Code uses 8bf178e1e1c2be3723f14683f7b1e5fdb3f780e4. Xstate uses cc087bf52cbe5cc291146816e7ef530bc53698a0 and its existing partial dependency installation. Existing workload diagnostics match exactly across main, before, and after; candidate semantic counts match before, including symbol counts. MUI check time improves 10.85% in these samples, but the shared host is noisy. No check-time difference against main is significant. This is not evidence that the hosted CI timing regression has been eliminated. heap-B/op is CLI-reported heap, not a forced-GC retained-heap measure. Cached and uncached declared/inherited enumeration, identity, ordering, negative lookups, unaffected snapshots, renamed keys, and diamond bases pass focused tests with race/checkptr. npx hereby validate --all passes, including 166161 Go tests and 954 API tests. Before and after this change: │ before.txt │ after.txt │ │ instructions/op │ instructions/op vs base │ mui-pinned-20 293.6G ± 2% 286.1G ± 2% -2.55% (p=0.002 n=6) webpack-20 53.35G ± 2% 52.12G ± 3% ~ (p=0.065 n=6) xstate-20 20.56G ± 2% 20.11G ± 1% -2.17% (p=0.015 n=6) vscode-20 300.9G ± 5% 301.5G ± 3% ~ (p=1.000 n=6) geomean 99.21G 97.52G -1.71% │ before.txt │ after.txt │ │ heap-B/op │ heap-B/op vs base │ mui-pinned-20 2.061Gi ± 0% 2.061Gi ± 0% ~ (p=0.394 n=6) webpack-20 666.6Mi ± 0% 666.7Mi ± 0% ~ (p=0.240 n=6) xstate-20 274.9Mi ± 0% 275.3Mi ± 0% +0.12% (p=0.002 n=6) vscode-20 2.834Gi ± 0% 2.833Gi ± 0% ~ (p=0.485 n=6) geomean 1.005Gi 1.005Gi +0.02% │ before.txt │ after.txt │ │ allocs/op │ allocs/op vs base │ mui-pinned-20 29.96M ± 0% 29.86M ± 0% -0.34% (p=0.026 n=6) webpack-20 6.287M ± 0% 6.238M ± 0% -0.78% (p=0.002 n=6) xstate-20 3.394M ± 0% 3.371M ± 0% -0.68% (p=0.002 n=6) vscode-20 36.92M ± 0% 36.67M ± 0% -0.68% (p=0.002 n=6) geomean 12.39M 12.32M -0.62% │ before.txt │ after.txt │ │ check-sec/op │ check-sec/op vs base │ mui-pinned-20 10.155 ± 27% 9.053 ± 12% -10.85% (p=0.041 n=6) webpack-20 1.844 ± 9% 1.743 ± 12% ~ (p=0.258 n=6) xstate-20 915.5m ± 9% 926.0m ± 9% ~ (p=1.000 n=6) vscode-20 7.689 ± 5% 7.549 ± 14% ~ (p=0.818 n=6) geomean 3.388 3.240 -4.36% │ main.txt │ after.txt │ │ instructions/op │ instructions/op vs base │ mui-pinned-20 317.0G ± 6% 286.1G ± 2% -9.74% (p=0.002 n=6) webpack-20 58.36G ± 9% 52.12G ± 3% -10.68% (p=0.002 n=6) xstate-20 23.45G ± 8% 20.11G ± 1% -14.23% (p=0.002 n=6) vscode-20 357.8G ± 10% 301.5G ± 3% -15.75% (p=0.002 n=6) geomean 111.6G 97.52G -12.63% │ main.txt │ after.txt │ │ heap-B/op │ heap-B/op vs base │ mui-pinned-20 2.680Gi ± 0% 2.061Gi ± 0% -23.11% (p=0.002 n=6) webpack-20 915.2Mi ± 0% 666.7Mi ± 0% -27.15% (p=0.002 n=6) xstate-20 393.7Mi ± 0% 275.3Mi ± 0% -30.09% (p=0.002 n=6) vscode-20 4.138Gi ± 0% 2.833Gi ± 0% -31.55% (p=0.002 n=6) geomean 1.397Gi 1.005Gi -28.05% │ main.txt │ after.txt │ │ allocs/op │ allocs/op vs base │ mui-pinned-20 32.84M ± 0% 29.86M ± 0% -9.07% (p=0.002 n=6) webpack-20 6.586M ± 0% 6.238M ± 0% -5.29% (p=0.002 n=6) xstate-20 3.804M ± 0% 3.371M ± 0% -11.39% (p=0.002 n=6) vscode-20 36.87M ± 0% 36.67M ± 0% -0.57% (p=0.002 n=6) geomean 13.20M 12.32M -6.67% │ main.txt │ after.txt │ │ check-sec/op │ check-sec/op vs base │ mui-pinned-20 9.032 ± 13% 9.053 ± 12% ~ (p=1.000 n=6) webpack-20 1.740 ± 8% 1.743 ± 12% ~ (p=1.000 n=6) xstate-20 985.5m ± 18% 926.0m ± 9% ~ (p=0.180 n=6) vscode-20 8.076 ± 10% 7.549 ± 14% ~ (p=0.240 n=6) geomean 3.344 3.240 -3.09%
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Contributor
|
Jake Bailey (@jakebailey) Here they are:
tscComparison Report - baseline..pr
System info unknown
Hosts
Scenarios
Developer Information: |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.