Skip to content

gh-150817: Speed up Flag bitwise operations#150824

Merged
ethanfurman merged 1 commit into
python:mainfrom
gaborbernat:opt/enum-flag-ops
Jun 4, 2026
Merged

gh-150817: Speed up Flag bitwise operations#150824
ethanfurman merged 1 commit into
python:mainfrom
gaborbernat:opt/enum-flag-ops

Conversation

@gaborbernat
Copy link
Copy Markdown
Contributor

@gaborbernat gaborbernat commented Jun 2, 2026

Combining Flag members with |, & and ^ walks both operands on every call to check that neither carries a None value, raising a clear TypeError when one does. That check only matters in the rare case where a value really is None; for every normal combination of two valid flags it adds work to each operation. Flag arithmetic is hot wherever flags model option sets — the re module's own flags are an IntFlag, so each re.compile(pattern, re.I | re.M) combines them, and permission systems, feature toggles and styling layers do the same in tight loops.

The two values the guard inspects are already in hand: one is the member's own value, the other is the operand value computed just above. This runs the scan over the operands only when one of those is actually None, so a valid combination skips it entirely. The offending-flag walk, the TypeError and its message are unchanged for the invalid cases.

Running flag combinations mined from the top-1000 corpus, including the re flag sets, improves from 72.6 µs to 49.5 µs, 47% faster.

Benchmark base patched
re.RegexFlag combine/and/in 56.5 µs 38.5 µs: 47% faster
IntFlag or/and/xor/in 72.6 µs 49.5 µs: 47% faster
Benchmark (pyperf)

Run base vs patched by swapping Lib/enum.py on the same interpreter. The flag combinations are real re flag sets mined from the top-1000 corpus.

import re, functools, operator, pyperf

combos_str = ["re.DOTALL|re.I", "re.DOTALL|re.M", "re.DOTALL|re.VERBOSE",
    "re.DOTALL|re.VERBOSE|re.M", "re.IGNORECASE|re.VERBOSE", "re.I|re.M",
    "re.I|re.S", "re.I|re.S|re.X", "re.MULTILINE|re.DOTALL",
    "re.MULTILINE|re.DOTALL|re.VERBOSE", "re.M|re.S", "re.S|re.M",
    "re.VERBOSE|re.I", "re.VERBOSE|re.M"]
M = {"I": re.I, "IGNORECASE": re.I, "M": re.M, "MULTILINE": re.M, "S": re.S,
     "DOTALL": re.S, "X": re.X, "VERBOSE": re.X, "A": re.A, "ASCII": re.A}
combos = [[M[p.split(".")[1]] for p in c.split("|")] for c in combos_str]

def run():
    for flags in combos:
        combined = functools.reduce(operator.or_, flags)
        flags[0] in combined

runner = pyperf.Runner()
runner.bench_func("re.RegexFlag combine %d real corpus combos" % len(combos), run)

Resolves #150817.

Flag.__or__, __and__ and __xor__ walked both operands on every call to reject
None values. Run that scan only when one of the operand values is actually
None, so valid combinations skip it. The TypeError and its message are
unchanged for the invalid cases.
@ethanfurman ethanfurman merged commit d4a8066 into python:main Jun 4, 2026
56 checks passed
@bedevere-bot
Copy link
Copy Markdown

⚠️⚠️⚠️ Buildbot failure ⚠️⚠️⚠️

Hi! The buildbot AMD64 FreeBSD14 3.x (tier-3) has failed when building commit d4a8066.

What do you need to do:

  1. Don't panic.
  2. Check the buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/#/builders/1232/builds/8948) and take a look at the build logs.
  4. Check if the failure is related to this commit (d4a8066) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/#/builders/1232/builds/8948

Failed tests:

  • test_logging

Failed subtests:

  • test_rollover_based_on_st_birthtime_only - test.test_logging.TimedRotatingFileHandlerTest.test_rollover_based_on_st_birthtime_only

Summary of the results of the build (if available):

==

Click to see traceback logs
Traceback (most recent call last):
  File "/home/buildbot/buildarea/3.x.opsec-fbsd14/build/Lib/test/test_logging.py", line 6666, in test_rollover_based_on_st_birthtime_only
    self.assertTrue(found, msg=msg)
    ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
AssertionError: False is not true : No rotated files found, went back 5 seconds

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Speed up Flag bitwise operations (|, &, ^)

3 participants