Skip to content

Speed up first incremental rebuilds after shared dependency edits - #64469

Draft
Gadzhi Gadzhiev (resure) wants to merge 2 commits into
microsoft:mainfrom
resure:fix/incremental-signature-performance
Draft

Gadzhi Gadzhiev (resure) wants to merge 2 commits into
microsoft:mainfrom
resure:fix/incremental-signature-performance

Conversation

@resure

@resure Gadzhi Gadzhiev (resure) commented Sep 26, 2026 •

Copy link
Copy Markdown

Fixes #64464

A comment-only edit to a widely imported module makes the first incremental rebuild far slower than a full check, even with noEmit. Two things cost the time. While emitting declaration signatures, the checker found the modules that can re-export a symbol by scanning every module's exports for every lookup. And the incremental builder computed the signatures of dependent files one file at a time.

The checker now keeps one index from a resolved export target to the modules exporting it, built on first use, plus a per-module cache of exports by target. The builder computes dependent signatures in parallel. On the issue's 6,000-leaf reproducer the first rebuild after the comment edit drops from 40.9 s to 1.5 s, with a byte-identical tsbuildinfo; peak RSS on that rebuild rises from 379 MiB to 501 MiB. Clean and second rebuilds are unchanged.

Two decisions worth a look. With parallel traversal, a changed file's signature may already have been computed by another changed file's traversal, so the cached-result path in updateShapeSignature now reports whether the signature actually changed instead of always false; otherwise a changed file that also affects global scope would no longer invalidate every file. Strada returns false there, but it is sequential and never hits this case. A unit test covers it. The checker index is published before it is filled, with a completion flag: a lookup that re-enters while the index is being built falls back to the old per-file scan rather than building it twice. No runtime path was found that triggers this; it is a guard.

Most of this change was generated with AI tooling; I have hit this problem myself and have reviewed the result.

🤖 Generated with Claude Code

@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Sep 26, 2026
@resure

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

The export-to-module index is now built directly from each module's
export table and published with a completion flag, so a lookup that
re-enters during the build falls back to the per-file scan instead of
building the index again. Per-module export buckets are sorted once when
created rather than on every lookup.

With dependent signatures computed in parallel, another changed file's
traversal can compute a changed file's signature first. The cached path
of updateShapeSignature now reports whether the signature changed, so a
changed file that affects global scope still invalidates every file.

The incremental scenario moves into the TestTscIncremental table and
also runs under --watch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

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

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

First incremental rebuild after a clean build is up to 75× slower than a full check

1 participant