Repository navigation
Speed up union construction with sorted-run merging and cheaper membership check - #65033
Open
Jake Bailey (jakebailey) wants to merge 5 commits into
Open
Jake Bailey (jakebailey) wants to merge 5 commits into
Jake Bailey (jakebailey) wants to merge 5 commits into
Conversation
addTypesToUnion collected the constituents of every source type into one list and then sorted the whole list with a stable sort. Most of that work is redundant: the constituents of each source union are already sorted by CompareTypes, and unions are very often built from just a few such lists (for example, a union plus `undefined`, or two unions). The full sort re-compares elements whose relative order is already known, and CompareTypes is not cheap for many kinds of types. Instead, note where each source type's sorted run begins out of order with the previous run, and merge those runs pairwise. When the input is already in order no sorting is done at all. Because CompareTypes is a total order that only reports equality for identical types, the merged result is exactly the list the stable sort produced. Co-authored-by: Steven Chong <[email protected]>
containsType is called frequently on union constituent lists (during relation checks, narrowing, and union reduction) and always did a binary search, paying for several CompareTypes calls even when the answer is obvious. Since type lists are sorted primarily by their sort order flags, a type whose flags fall outside the range spanned by the first and last elements cannot be in the list, which can be decided with two cheap flag reads. For short lists, a linear identity scan is also cheaper than the comparisons a binary search requires. Co-authored-by: Steven Chong <[email protected]>
When reducing a union, removeRedundantLiteralTypes drops a fresh literal type if its regular counterpart is also present, and did so by binary searching the whole type set for every fresh literal. Unions of many fresh literals (common for inferred literal-heavy expressions) therefore paid a full search per member. A fresh literal type and its regular type have the same flags, symbol and value, so CompareTypes only distinguishes them by type ID, and no other type can sort between them. The regular type, if present, must therefore be immediately before or after the fresh type in the sorted set, which can be checked directly. Co-authored-by: Steven Chong <[email protected]>
When a union contains both string literals and pattern template literal types, every string literal is tested against every template to see whether it is subsumed. Each test goes through template literal inference, which allocates and does a fair amount of work before discovering that most literals do not even begin and end with the template's fixed leading and trailing text. Check those fixed texts directly first. This is the same condition inference rejects on, so only literals that could actually match pay for the full check. Co-authored-by: Steven Chong <[email protected]>
Member
Author
|
TypeScript Bot (@typescript-bot) perf test this faster |
Contributor
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Reslicing the run-index buffer to zero retains arbitrarily large allocations for the checker’s lifetime, even when deduplication leaves only two union constituents.
1 open issue
What changed in this PR
This PR optimizes union construction and membership checks in the native TypeScript checker.
Changes:
- Merge already-sorted constituent runs instead of sorting the entire union.
- Use adjacent checks for fresh literal counterparts and cheaper membership checks for short lists.
- Reject impossible string-literal/template matches before running full matching logic.
| File | Description |
|---|---|
tsc/internal/checker/checker.go |
Adds sorted-run merging, reusable run-index storage, and faster union membership and literal-reduction checks. |
Review effort: Balanced
The run-start buffer is kept on the checker for reuse across union constructions. Inputs with many short sorted runs, such as alternating primitives, can grow it far beyond the size of the resulting union, and that capacity would then be retained for the checker's lifetime. Discard buffers above the same 4096-element cutoff used for temporary type sets, while still reusing smaller ones.
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.

Thanks Steven (@teamchong) for finding these!