python / python/cpython

Improve performance of `re.compile()` for patterns with character ranges

Aperta
#149,427 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

performance stdlib topic-regex type-feature
Lingua principale
Python
Stelle
77.2k
Fork
35.9k
Metriche di merge delle PR
Metriche PR in attesa

Descrizione

Feature or enhancement

Proposal:

re._compiler._optimize_charset builds a 256-byte (or wider) charmap for character classes. On main it is filled with a loop:

for i in range(av[0], av[1] + 1):
    charmap[i] = 1

Replacing the loop with a single bytearray slice is faster.

Benchmark results on pyperformance regex_compile

main (86.2 ± 1.4 ms)  ->  PR (78.5 ± 1.6 ms):  1.10x faster

Microbenchmarks:

compile-charsets-small:  2.80 ± 0.04 ms  ->  1.22 ± 0.02 ms:  2.29x faster
compile-charsets-big:    1.46 ± 0.03 ms  ->  1.36 ± 0.03 ms:  1.08x faster
Geometric mean:                                              1.57x faster

The "small" case oversamples wide ranges ([\x00-\xff], BMP range) which benefit most. The "big" case has mostly small ASCII ranges where the slice-fill construction (b'\x01' * n) competes with the loop savings.

microbench script
"""Microbench for the bytearray slice-fill optimization in
re._compiler._optimize_charset (RANGE handler)."""
import pyperf
import re

SMALL_PATTERNS = [
    r'[a-zA-Z0-9_]+',
    r'[^\s\d]+',
    r'[a-z][A-Z][0-9]',
    r'[\x00-\xff]',                  # full byte range
    r'[Ā-῿]+',                       # BMP range, hits growth path
    r'[a-zA-Z0-9!@#$%^&*()_+\-=\[\]{};:\'",.<>/?\\|`~]+',
    r'[0-9a-fA-F]{2,8}',
    r'[ -~]+',                       # all printable ASCII
]

BIG_PATTERN = (
    r'^([a-zA-Z][a-zA-Z0-9_\-]*)\s*'
    r'([\x20-\x7e]*)\s*'
    r'([-ÿĀ-ɏ]+)?\s*'
    r'([0-9]{1,4}[\-/.][0-9]{1,2}[\-/.][0-9]{1,4})?\s*'
    r'([^\s,;:]+)$'
)

def compile_small():
    for _ in range(8):
        for p in SMALL_PATTERNS:
            re.purge()
            re.compile(p)

def compile_big():
    for _ in range(10):
        re.purge()
        re.compile(BIG_PATTERN)

runner = pyperf.Runner()
runner.bench_func('compile-charsets-small', compile_small)
runner.bench_func('compile-charsets-big', compile_big)
Has this already been discussed elsewhere?

No response given

Links to previous discussion of this feature:

No response

Linked PRs
  • gh-149428

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia da re._compiler._optimize_charset, concentrandoti sull'handler RANGE che riempie charmap. Esamina la PR collegata gh-149428 e confronta il suo comportamento per intervalli di caratteri piccoli e ampi. Il lavoro è completato quando la gestione degli intervalli rimane corretta e regex_compile e i microbenchmark forniti riproducono il miglioramento delle prestazioni previsto.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
python
Ambito
performance
Tipo di issue
Funzionalità
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Ferma
Chiarezza
Specificata chiaramente
Idoneità per principianti
25/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.