Repository navigation
perf: close remaining regex performance gap vs native Perl - #1908
Merged
Merged
Conversation
… toString - Cache PerlRuntime.current() in matchRegexDirect to eliminate redundant ThreadLocal read for taint-mode check - Add INTEGER to RuntimeScalar.toString() inline fast path, avoiding dispatch through toStringLarge() for the common integer-regex case - Add regression test covering ASCII fast path, identity array pool boundaries, integer matching, and Unicode fallback Benchmark: ~24,000/s (up from ~22,100/s baseline, native Perl ~50,000/s) Closes #1907 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Literal pattern fast path: patterns like /42/ that contain no metacharacters bypass the entire Joni engine and use String.indexOf() directly. LiteralMatcher implements RegexMatcher to provide correct $-[0]/$+[0]/match-state after a literal hit. Merged two per-match ThreadLocal reads (setCallSiteWarningState + setCallSiteWarningBits) into a single combined setCallSiteWarning() call with one shared CallSiteWarningHolder object. Integer string cache (0-511) avoids Integer.toString() allocation for small non-negative values common in loop counters. Benchmark: ~47,000 iter/s vs native Perl ~50,000 iter/s (1.07x gap, down from 1.8x at issue open). Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Perl's taint rules: match results ($`, $&, $') are tainted only when the pattern itself is tainted, locale is involved, or the taintResults flag is set — not when only the target string is tainted. The literal fast path was incorrectly setting lastMatchResultsTainted based on the target string's taint status, causing 3 failures in op/taint.t (tests 478-480). Fix: set lastMatchResultsTainted = false unconditionally in matchLiteralScalar (literal patterns are compile-time constants, never tainted), and add a taintResults() guard to the fast path eligibility check so regex patterns with that flag always take the full Joni path. Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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
Closes the remaining regex performance gap identified in #1907. Five rounds of optimizations targeting the regex hot path:
Round 1 (
03977c3): Reduce per-match regex overhead by ~67%INPUT_ENCODINGSLRU cache for short ASCII stringsisAscii()+asciiToBytes()to avoidgetBytes(UTF_8)allocationRound 2 (
4da9f7c): ~2x total speedupcharToByte/byteToChar) up to 64 elementsbyteInputEncoding()for short stringsRound 3 (
9c44310): Skip identity array build for ASCIIbuildInputEncoding()fallbackbuildByteInputEncoding()Round 4 (
3b00c2c): Reduce ThreadLocal reads, fast INTEGER toStringPerlRuntime.current()inmatchRegexDirectto eliminate redundant ThreadLocal read for taint-mode checkINTEGERtoRuntimeScalar.toString()inline fast pathRound 5: Literal fast path, merged ThreadLocal, integer string cache
/42/) bypass the Joni engine entirely usingString.indexOf()LiteralMatcherimplementsRegexMatcherfor correct$-[0]/$+[0]after a literal matchsetCallSiteWarningState+setCallSiteWarningBits) into single combined call with shared holder objectInteger.toString()allocation for small loop countersResolvedRegexrecord allocation)Fix (
ad62884): Use Region-aware matcher when regex has capture groupsBenchmark results
Test plan
make— BUILD SUCCESSFUL)regex_hot_path_perf.tcovers ASCII fast path, identity array pool boundaries, integer matching, Unicode fallback, captures, literal match positions ($-[0]/$+[0]/$&), and edge cases (37 tests)🤖 Generated with Claude Code