perf: accelerate safe API hot paths - #100
Merged
Merged
Conversation
Qubitium
marked this pull request as ready for review
August 9, 2026 06:51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Summary
This PR targets safe API-level speedups after PR #99, preserving CPython 3.10 compatibility and free-threaded CPython 3.14t/GIL=0 behavior.
findall,substitute,finditer, andsplitshapes.findallscan while retaining worker-local PCRE2 state and the owned subject; subsequent match/object construction stays protected.span/start/end/regsoffset conversion constant-time instead of rescanning the UTF-8 prefix.Match.expandconstant-time and adds a bounded, clearable LRU for immutable replacement-template parsing/conversion.parallel_mapwork into ordered chunks, reducing Future allocation and queue-lock overhead without changing result order or exception propagation.hw.perflevel0.logicalcputo permit safe explicit parallelism across the 12-logical performance cluster while retaining conservative fallbacks elsewhere.match,search,fullmatch,findall,finditer,split, and literalsub/subn.Match.groups()tuple; custom defaults remain fresh and do not retain caller objects.Match.expand()template parsing by template, capture count, and a snapshot of the current name table; rendering remains per-match and per-call.compile(str|bytes)calls, preserving thread-local wrapper ownership and cache-limit semantics.Flagenums remain on the hardened general cache for free-threaded safety.parallel_mapcalls while retaining the list-shaped API and auto-mode subject validation.Flag.THREADS, avoiding queue setup while leaving auto-threshold behavior unchanged.Pattern.splitcalls; subclass, buffer, and bounded-limit paths remain unchanged.Pattern.subn; escaped, callable, subclass, buffer, and bounded-count replacements remain on the compatibility path.findall,split, andsub/subn; regex metacharacters, explicit flags, subclasses, buffers, and negative-count semantics stay on the compatibility path.subncalls through the same count-correct bound fast path; the existing PCRE2 substitute entry point remains unchanged for non-literal patterns.group,span,start,end, andregs).benchmarks/api_hotpaths.pyandbenchmarks/parallel_map_hotpath.pymeasurements.Performance discovery
Pinned A/B runs use the same macOS
taskpolicy -t 1 -l 1scheduler policy. This host reports 12 performance logical CPUs and 4 efficiency logical CPUs; macOS does not expose an unprivileged hard per-process CPU mask, so the benchmark records the topology and uses the same performance-tier policy for both runs.match0.408 / 0.338,search0.412 / 0.342,fullmatch0.427 / 0.351,findall0.639 / 0.558,finditer0.774 / 0.792,split0.613 / 0.576, and literalsub0.961 / 0.871. This is about 2.5–3.1x faster than the pre-dispatch module hot path and compounds with the earlier C speedups.Match.groups()with its default argument is now an immutable per-match tuple cache; repeated calls measure about 0.019 us on Python 3.10 and 0.022 us on Python 3.14t.Match.expand(r"[\\1]")measures about 0.94 us on Python 3.10 and 0.70 us on Python 3.14t after eliminating repeated parser and piece-list work.compile("(x)")measures about 0.34 us on Python 3.10 and 0.25 us on Python 3.14t after avoiding general cache-key and wrapper dispatch overhead.compile("x", CASELESS)measures about 0.84 us on Python 3.10 and 0.58 us on Python 3.14t.parallel_map(findall)avoids executor setup (13.3x faster on Python 3.10 and 27.7x on Python 3.14t in the measured microbench).Pattern.splitmeasures about 0.35 / 0.33 us on Python 3.10 / 3.14t versus about 0.59 / 0.50 us through the compatibility path.Pattern.subnmeasures about 0.43 us on Python 3.10, down from about 0.64 us before the shortcut.findallmeasures about 2.0 us versus 19.3 us on a 1,000-byte repeated-token subject, and about 301 us versus 2.4 ms on delimiter-heavy text; the no-match first-character short-circuit is also faster than PCRE2 scanning.subnmeasures about 1.1 us on a 1,000-byte repeated-token subject versus about 15.8 us through the prior C path, and about 0.8 ms versus 2.4 ms on delimiter-heavy text.parallel_map(search), 16 one-million-character subjects, 12 workers: 8.57x on Python 3.10 and 7.85x on Python 3.14t/GIL=0.parallel_map(findall), 48 one-million-character subjects, 12 workers: 11.51x on Python 3.10 and 11.25x on Python 3.14t/GIL=0.match/search/fullmatch/findallcalls after owner-aware C vectorcall dispatch.Already PCRE2-dominated bound
match/searchcalls remain near their C floor; they are left unchanged where extra dispatch would trade away accuracy or safety.Safety and validation
ruff format --checkandgit diff --checkpass; the extension builds cleanly for both interpreters.All fast paths preserve type checks, PCRE2 replacement escaping, match ownership, ordered results, exception behavior, and thread-safe resource handling. The large-scan path releases the GIL only around PCRE2 work and never exposes mutable Python state to the matcher.