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 in membership tests and for loop iteration. This causes the collection to be rebuilt from scratch on every evaluation instead of being stored as a compile-time constant.
For example:
import dis
# 30 elements: folded to frozenset constant
dis.dis("x in {%s}" % ", ".join(str(i) for i in range(1, 31)))
# LOAD_NAME x
# LOAD_CONST frozenset({1, 2, ..., 30})
# CONTAINS_OP
# 31 elements: NOT folded, set rebuilt every time (BAD)
dis.dis("x in {%s}" % ", ".join(str(i) for i in range(1, 32)))
# LOAD_NAME x
# BUILD_SET 0
# LOAD_SMALL_INT 1
# SET_ADD 1
# ... (31 x LOAD + SET_ADD)
# CONTAINS_OP
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:
import timeit, sys
elts = ", ".join(str(i) for i in range(50))
t = timeit.timeit(f"25 in {{{elts}}}", number=1_000_000)
print(f"25 in {{0..49}}: {t:.3f}s")
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:
for n in [30, 31, 50, 100]:
elts = ", ".join(str(i) for i in range(1, n + 1))
code = compile(f"x in {{{elts}}}", "<test>", "eval")
print(f"x in {{1..{n}}}: {len(code.co_code):>5} bytes, {len(code.co_consts)} const(s)")
Python 3.13.13:
x in {1..30}: 12 bytes, 1 const(s)
x in {1..31}: 12 bytes, 1 const(s)
x in {1..50}: 12 bytes, 1 const(s)
x in {1..100}: 12 bytes, 1 const(s)
Python 3.14.4:
x in {1..30}: 12 bytes, 2 const(s)
x in {1..31}: 136 bytes, 1 const(s)
x in {1..50}: 212 bytes, 1 const(s)
x in {1..100}: 412 bytes, 1 const(s)
CPython versions tested on:
3.15, CPython main branch, 3.14
Operating systems tested on:
macOS, Linux
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