Repository navigation
sets with >30 elements not constant-folded in in/not in and for loops #148817
Description
Activity
- addedtype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or errorperformancePerformance or resource usagePerformance or resource usageinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)
on Apr 21, 2026 How likely are there sets of 30+ elements that need to be stored as real constants? I can see the set of ascii letters+digits being such examples thereof or essentially unicode ranges. What would be a good cutoff here? 50? 100? 70? 52? 62? (I would say 64 just to have lower/upper case + digits)
- added3.14bugs and security fixesbugs and security fixes3.15bugs and security fixesbugs and security fixes
on Apr 21, 2026 How likely are there sets of 30+ elements that need to be stored as real constants?
I came across this because it came up in real world code :)
I ran some analyses and found at least 95 files with this pattern (with set/list literals with more than 30 elements). most were in the 30-50 elements range.
here's one example I found from numpy (with 51 elements): https://github.com/numpy/numpy/blob/388bd75829842d8230147f57eba5f86bb3ad6c3c/numpy/f2py/crackfortran.py#L253-L261
beyond that, I had a small number of hits with collections of sizes 51 to 120.
I think having 64 as a cutoff would cover most use cases. It covers alnum ranges. We can make it 256 as well just to cover the entire ASCII range (conceptually). What is the cost of having a much larger length? like, it would only slow down the bytecode generation (so negligible cost), and bump the process' memory for those constant-folded objects right?
Do you know which commit introduced this?
@picnixz re cutoff -- I am not knowledgeable enough to say, as I don't fully understand the tradeoffs.
based on asking Claude Code to analyze this more closely, it claims that the original behavior (AST optimizer) had no limit when constant-folding, and even now,
fold_constant_intrinsic_list_to_tuplewould constant-fold tuples with no size limit. the "cost" of unlimited constant-folding would be compile time, and presumably shifting such cost from runtime to compile time is almost always beneficial (and would also net a small pyc size win since a marshalled frozenset of size N would be smaller than the equivalent sequence of bytecode to dynamically construct it at runtime).Claude Code went further and implement the optimization (
fold_const_set_or_list_add, based onfold_constant_intrinsic_list_to_tuple), but given my lack of expertise in this area, I can't "own" that optimization to the level I would be comfortable making a PR based on it (I can push it to a branch in my fork if someone else might find it useful though).I don't think we need any cutoff here, runtime performance matters much more than bytecode generation.
Reacted by Bénédikt Tran, Itamar Oren, Bartosz Sławecki, Maurycy Pawłowski-Wieroński, Ian Skliarov and Dmitry AgafonovYeah, that's certainly because of moving things from ast_opt.c to flowgraph.c. In flowgraph.c we basically use a compile-time constant
_PY_STACK_USE_GUIDELINEfor almost all of those optimizations, and by default it's equal to 30, so we don't optimize sequences longer than that. And yes, this was an intentional change, sorry if we forgot to mention it explicitly.@itamaro I'd be happy to take a look at your PR =)
@itamaro I'd be happy to take a look at your PR =)
@Eclips4 see itamaro@6c9b972 with the disclaimer that it's fully claude-authored. I ran it, but I didn't look at it!
Thanks Itamar!
I'll do a PR on the weekend.Reacted by Itamar OrenFYI folks I'm going to merge #146456 to make things faster when we have long sequences with constants. Otherwise we could get bad compilation times in those cases.
Reacted by Itamar OrenPR #149016 is up. @iritkatriel @itamaro, I'd really appreciate a review and verification from you.
Reacted by Itamar Oren- added a commit that references this issue
on Aug 19, 2026
Bug report
Bug description:
Since gh-126835 (moving constant folding from AST to CFG), set and list literals with more than 30 constant elements are no longer constant-folded in
in/not inmembership tests andforloop iteration. This causes the collection to be rebuilt from scratch on every evaluation instead of being stored as a compile-time constant.For example:
Using a set-membership check with a set literal with 50 elements as a microbenchmark, this manifests as a ~20x slowdown from 3.13 to 3.14:
Python 3.13.13: 25 in {0..49}: 0.015s
Python 3.14.4: 25 in {0..49}: 0.291s
It also affects code object size:
Python 3.13.13:
Python 3.14.4:
CPython versions tested on:
3.15, CPython main branch, 3.14
Operating systems tested on:
macOS, Linux
Linked PRs