david-mollitor-db opened a new pull request, #59223:
URL: https://github.com/apache/spark/pull/59223

   ### What changes were proposed in this pull request?
   
   When the pattern of `RLIKE` or `LIKE` is foldable, the generated code 
compiles the `Pattern` once into mutable state, but still calls 
`pattern.matcher(input)` on every row. This PR builds the `Matcher` once in the 
mutable state's init code and resets it per row instead:
   
   ```java
   // before
   value = patternRLike.matcher(input.toString()).find(0);
   
   // after; init code: matcherRLike = Pattern.compile("<regex>", 
<flags>).matcher("");
   value = matcherRLike.reset(input.toString()).find(0);
   ```
   
   `LIKE` gets the same change, using `matches()`. It applies to patterns that 
`LikeSimplification` does not rewrite into `StartsWith` / `EndsWith` / 
`Contains`, such as `a%b%c`.
   
   - The Matcher replaces the Pattern field (a Matcher holds its Pattern), so 
the number of mutable states does not change.
   - Codegen mutable state belongs to one generated-object instance, so the 
Matcher is never shared between threads. The child is evaluated before `reset`, 
and each match finishes before the next row starts.
   - Not changed: the interpreted path (`RLike` / `Like` are not `stateful`, so 
a Matcher cached on the expression would be shared), non-foldable patterns, 
`LIKE ANY` / `LIKE ALL`, and the per-row `UTF8String.toString()`.
   
   ### Why are the changes needed?
   
   Each `pattern.matcher(...)` call allocates a `Matcher` along with its 
`groups` and `locals` `int[]` and its `localsPos` `IntHashSet[]`. The JIT 
cannot scalar-replace these objects because the Matcher escapes into the regex 
engine's recursive `Node.match` calls. In allocation profiles of a filter with 
a token-boundary `RLIKE` (`(?<![a-z0-9])word(?![a-z0-9])`), `Matcher.<init>` <- 
`Pattern.matcher` <- generated `processNext` accounted for 2-5% of allocated 
bytes.
   
   Local benchmark: JDK 17, `local[1]`, 5M cached rows of short strings such as 
`item 123 sword tail`, each case run as `selectExpr(<predicate>).noop()`, with 
before and after runs interleaved. Values are ns per row:
   
   | Case | Before (3 runs) | After (2 runs) | Change |
   |---|---|---|---|
   | control: `s LIKE '%tail'` (rewritten to `EndsWith`, not affected) | 50.4 - 
52.9 | 50.9 - 51.6 | none |
   | `s RLIKE '(?<![a-z0-9])word(?![a-z0-9])'` | 266.4 - 268.5 | 251.0 - 253.4 
| -6% |
   | `s RLIKE '^item 1'` | 89.8 | 85.3 | -5% |
   | `s LIKE 'item%word%tail'` | 240.7 - 248.1 | 233.2 - 236.0 | -4% |
   
   One more "after" run, taken right after a build, was slower on every case, 
the control included, so it is left out.
   
   Under JFR, sampled allocation for the whole benchmark run fell from 67.0 GB 
to 27.3 GB. In the before run, `int[]`, `java.util.regex.Matcher` and 
`IntHashSet[]` together made up about 70% of allocation pressure. None of them 
appear among the top allocating classes in the after run.
   
   This benchmark is not part of the PR. I can add cases to 
`StringFunctionsBenchmark` if reviewers want them.
   
   ### Does this PR introduce _any_ user-facing change?
   
   No. Only performance changes.
   
   ### How was this patch tested?
   
   - New test in `RegexpExpressionsSuite`: it sends a sequence of rows through 
one generated projection that holds two `RLIKE` expressions and one `LIKE`. The 
rows include matches, non-matches, a null, an empty string, and a long input 
followed by a short one, and the test checks that no result depends on the rows 
before it.
   - Existing `RegexpExpressionsSuite`, `CollationRegexpExpressionsSuite` and 
`CollationSQLRegexpSuite` pass, as do the `regexp-functions.sql` and 
`like-*.sql` golden files in `SQLQueryTestSuite`.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Isaac (Claude Code, Claude Opus 5.5)
   
   This pull request and its description were written by Isaac.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to