[ty] Promote Literal types when inferring elements for very large unannotated tuples#22841
[ty] Promote Literal types when inferring elements for very large unannotated tuples#22841AlexWaygood merged 1 commit intomainfrom
Literal types when inferring elements for very large unannotated tuples#22841Conversation
Typing conformance resultsNo changes detected ✅ |
Literal types when inferring elements for very large unannotated tuplesLiteral types when inferring elements for very large unannotated tuples
|
0688102 to
4caefe8
Compare
Merging this PR will improve performance by ×4.8
Performance Changes
Comparing Footnotes
|
fbb0962 to
7d05ff2
Compare
689c00c to
e47926e
Compare
| /// If a tuple literal has more elements than this constant, | ||
| /// we promote `Literal` types when inferring the elements of the tuple. | ||
| /// This provides a huge speedup on files that have very large unannotated tuple literals. | ||
| const MAX_TUPLE_LENGTH_FOR_UNANNOTATED_LITERAL_INFERENCE: usize = 64; |
There was a problem hiding this comment.
64 is a small number[citation needed], but I think it should easily be big enough. I wondered about setting it even lower, e.g. 32. I think there are vanishingly few cases where it's useful to have Literal types preserved for the subelements of a large tuple.
carljm
left a comment
There was a problem hiding this comment.
This looks good to me, and I'm fine just going with it.
I do wonder if we couldn't get the same speedup by applying the literal promotion at the point where we are unioning all the tuple members, rather than when we construct the tuple type? Then you'd still get precise type on subscript, just not on iteration/unpacking (where we need the homogenized type).
I think that's possible, yeah. But I'm still not sure what the actual use case is? I can't think of a situation where I'd benefit from getting a precise |
e47926e to
262acd5
Compare
Fixes astral-sh/ty#71. Stacked on top of #22842.
This yields a huge speedup locally when checking https://github.com/MMD-Blender/blender_mmd_tools/blob/6ae13d6039763b6813832622c13da464983e93b3/mmd_tools/m17n.py. The pathological performance on that file wasn't due to inferring a type for the large tuple (that is very fast) -- it was inferring types when subscripting and unpacking that tuple that caused issues. If the tuple is inferred as containing many varied
Literaltypes, subscription and unpacking operations on the tuple involve mapping type operations over huge unions. If the tuple is instead inferred as containing types likeint,strandbytes, however, the unions inferred are much smaller.Promoting
Literaltypes in a very large tuple means that you don't get preciseLiteraltypes out of the tuple when you subscript it. But I think it's very unlikely that you'd want or need a preciseLiteraltype when subscripting a tuple of >64 elements. And if you do... you can give us a type hint, and we'll still respect your intent. (There's some examples in the tests I've added: we'll even respect type hints liketuple[Literal[1], Literal[2], *tuple[int, ...], Literal[65]], and we'll inferLiteraltypes for only the first, second and last elements of the tuple.)As Eric noted in astral-sh/ty#71 (comment), pyright also has a fallback like this to avoid pathological performance for large tuples, but pyright's fallback is much more lossy fallback: it falls back to
tuple[Unknown, ...]for any tuple >256 elements long. I'd prefer not to do that unless we have to, and it seems to me that what I'm proposing here fixes all known pathological performance issues with large tuples without a significant sacrifice of precision.