Skip to content

sets with >30 elements not constant-folded in in/not in and for loops #148817

@itamaro

Description

@itamaro

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)performancePerformance or resource usagetype-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions