From 586eec8826ef20d1a6b9c1de3b9fd17e261d0f6f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Sat, 17 Oct 2026 09:07:26 -0700 Subject: [PATCH] Store merged symbols in a paged symbol link store MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit getMergedSymbol runs on nearly every symbol access, and almost every call misses because few symbols are merge sources. Each miss still paid for a pointer-keyed map hash and probe. Indexing by dense symbol ID makes the common miss a range check and a page load. --checkers 4, n=8, default GC settings: │ before │ after │ │ sec/op │ sec/op vs base │ Tsc/bluesky 2.799 ± 2% 2.767 ± 1% ~ (p=0.266 n=8) Tsc/sentry 10.86 ± 2% 10.78 ± 2% ~ (p=0.314 n=8) Tsc/t3code-server 13.89 ± 6% 13.50 ± 4% -2.85% (p=0.050 n=8) geomean 7.502 7.384 -1.58% │ before │ after │ │ rss-B │ rss-B vs base │ Tsc/bluesky 1.203Gi ± 2% 1.183Gi ± 2% ~ (p=0.291 n=8) Tsc/sentry 3.535Gi ± 6% 3.549Gi ± 6% ~ (p=1.000 n=8) Tsc/t3code-server 2.877Gi ± 3% 2.880Gi ± 2% ~ (p=0.721 n=8) geomean 2.304Gi 2.296Gi -0.37% │ before │ after │ │ heap-B │ heap-B vs base │ Tsc/bluesky 934.8Mi ± 0% 934.5Mi ± 0% -0.04% (p=0.038 n=8) Tsc/sentry 2.603Gi ± 0% 2.608Gi ± 0% +0.16% (p=0.000 n=8) Tsc/t3code-server 2.312Gi ± 0% 2.301Gi ± 0% -0.45% (p=0.000 n=8) geomean 1.765Gi 1.763Gi -0.11% --- tsc/internal/checker/checker.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 10ddb3c0c88b4..2a1985ac572c1 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -692,7 +692,7 @@ type Checker struct { structuredTypeExtraArena core.Arena[structuredTypeExtra] freeTypeSets *temporaryTypeSet freePropertyCounts *temporaryPropertyCounts - mergedSymbols map[*ast.Symbol]*ast.Symbol + mergedSymbols symbolLinkStore[*ast.Symbol] mergedExportsChecked collections.Set[*ast.Symbol] factory ast.NodeFactory nodeLinks core.LinkStore[*ast.Node, NodeLinks] @@ -1003,7 +1003,6 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.unionOfUnionTypes = make(map[UnionOfUnionKey]*Type) c.intersectionTypes = make(map[CacheHashKey]*Type) c.propertiesTypes = make(map[PropertiesTypesKey]*Type) - c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol) c.patternForType = make(map[*Type]*ast.Node) c.contextFreeTypes = make(map[*ast.Node]*Type) c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") @@ -14624,9 +14623,8 @@ func (c *Checker) cloneSymbol(symbol *ast.Symbol) *ast.Symbol { func (c *Checker) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol { if symbol != nil { - merged := c.mergedSymbols[symbol] - if merged != nil { - return merged + if merged := c.mergedSymbols.TryGet(symbol); merged != nil && *merged != nil { + return *merged } } return symbol @@ -14640,7 +14638,7 @@ func (c *Checker) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol { } func (c *Checker) recordMergedSymbol(target *ast.Symbol, source *ast.Symbol) { - c.mergedSymbols[source] = target + *c.mergedSymbols.Get(source) = target } func (c *Checker) getResolvedTarget(symbol *ast.Symbol) *ast.Symbol {