GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-161125: Avoid redundant work in regex compilation by SHURIKA6 · Pull Request #161600 · python/cpython

Conversation

@SHURIKA6

@SHURIKA6 SHURIKA6 commented Nov 8, 2026 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

Summary

re.compile() on main is about 16-19% slower than 3.15 for ordinary
patterns. The regression comes from gh-152214, which added a set-operation
fusion pass to the pattern compiler (re._optimizer). That pass walks the
whole parsed tree unconditionally, looking for ||, && and --
constructs. Patterns without those constructs pay for the walk and get
nothing back.

This PR lets the parser record whether the tree contains anything the
fusion pass can actually rewrite, and skips the walk when it does not.

Root cause

Since gh-152214, re._optimizer.optimize() always calls _walk():

def optimize(pattern):
    _walk(pattern.data)
    return pattern

_walk() recurses over every node of the parsed tree and calls the
peepholes (_fuse_difference, ...) on each node. For a pattern like
\d{4}-\d{2}-\d{2} there is nothing to fuse: the walk visits the whole
tree and rewrites nothing. Profiling re.compile() over a corpus of 110
distinct patterns collected from the stdlib and site-packages shows no
pattern that the fusion pass rewrites.

Approach

The parser is the only place that knows whether a fusable construct is
present, so it records that in State.fusable:

  • State.fusable starts as None, meaning "unknown": the tree was not
    produced by parse() (for example, re.Scanner assembles one by hand).
    The optimizer must still walk those trees.
  • parse() sets it to False, and the parse paths that can produce
    something the optimizer understands set it to True: | between
    one-character alternatives (_parse_sub, _union), -- (_difference)
    and negative lookbehind ((?<!...)).
  • optimize() walks only when the marker is not False.

Behaviour is unchanged: for 1504 patterns (both str and bytes, taken
from the stdlib, site-packages and test_re), the bytecode produced by
main and by this branch is identical, and the same 8 patterns fail to
compile with the same errors.

Skipping the walk based on a plain boolean would change behaviour for
hand-assembled trees: re.Scanner builds patterns without calling
parse(), and those still need optimization. The tri-state marker keeps
that path working.

Benchmarks

The official pyperformance regex_compile benchmark, run with pyperf
on Release builds, CPU affinity pinned to one core, Windows 11,
i5-12450H, CPython 3.16.0a1+ (a497a11c46):

build run 1 (mean ± sd) run 2 (mean ± sd)
main a497a11c46 263.8 ± 25.1 ms 287.4 ± 44.7 ms
main + this PR 228.3 ± 33.9 ms 241.4 ± 22.1 ms
3.15.2 (reference) 235.8 ± 18.0 ms 293.7 ± 53.4 ms

Paired by run, this branch is 1.16x-1.19x faster than main and back in
the range of 3.15.2. The machine is noisy (the 3.15.2 runs differ by 25%
between themselves), so I would not read anything into the remaining
few percent.

Tests

  • python -m test test_re: 173 tests, 4 skipped, all pass.
  • New test test_optimizer_walk_only_for_fusable_patterns pins the
    parser marker for fusable and non-fusable patterns, and checks that a
    hand-built State() keeps the "must optimize" default.

AI assistance

Developed with AI assistance for the analysis, the patch draft and the
benchmark scripts. I reviewed every line, ran the tests and benchmarks
myself, and can explain the change in my own words.

  • Issue: Regression in re.compile() performance #161125

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@SHURIKA6
pythongh-161125: Avoid redundant work in regex compilation
c38c373
pythongh-152214 added a set-operation fusion pass to the pattern compiler.
optimize() walks the whole parsed tree unconditionally, so patterns that
contain no fusable construct pay for a full extra pass and get nothing in
return. On main this makes re.compile() about 15-20% slower than 3.15.

Have the parser record whether the tree holds a construct that the
fusion pass can rewrite, and walk only in that case. The marker stays
None for trees that were not produced by parse() (such as the ones
re.Scanner assembles by hand), so those are still optimized as before.
@python-cla-bot

python-cla-bot Bot commented Nov 8, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown

All commit authors signed the Contributor License Agreement.

CLA signed

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot added the awaiting review label Nov 8, 2026
@bedevere-app bedevere-app Bot mentioned this pull request Nov 8, 2026
Regression in re.compile() performance #161125
Open
@picnixz picnixz added the AI-guidelines-not-met The contribution does not meet our guidelines for using AI tools and is closed automatically. label Nov 8, 2026
@bedevere-app

bedevere-app Bot commented Nov 8, 2026

Copy link
Copy Markdown

Thank you for your interest in contributing to Python. This is a community-driven project, and we want it to be a welcoming place to learn and collaborate.

Contributors are responsible for reviewing and understanding what they submit, checking its claims, and being able to explain changes in their own words. This helps reviewers focus on the contribution and leaves more time to support other contributors.

We believe this contribution does not meet our guidelines for using AI tools, so we're closing it. If you think we've misunderstood your contribution, please let us know. You're welcome to explain your approach or ask questions about the guidelines.

Please read our guidelines for using AI tools.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@bedevere-app bedevere-app Bot closed this Nov 8, 2026
@picnixz

picnixz commented Nov 8, 2026

Copy link
Copy Markdown
Member

A core dev is more likely to address this.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@SHURIKA6

SHURIKA6 commented Nov 8, 2026

Copy link
Copy Markdown
Author

A core dev is more likely to address this.

But do you think you're right?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@picnixz

picnixz commented Nov 8, 2026

Copy link
Copy Markdown
Member

The issue hasn't been open for enough time and no consensus on how to fix this has been made, so yes, a core dev must first acknowledge the direction of the fix.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

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

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

AI-guidelines-not-met The contribution does not meet our guidelines for using AI tools and is closed automatically. awaiting review

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

2 participants

@SHURIKA6 @picnixz
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.