Uh oh!
There was an error while loading. Please reload this page.
Eliminate backtracking in the interpreter for patterns with .* - #51508
Conversation
ghost
commented
Apr 19, 2021
Tagging subscribers to this area: @eerhardt, @pgovind Issue DetailsI spend the last week looking at some potential optimizations in the RegexInterpreter and found this improvement. This PR doesn't change any behavior and is a straightforward optimization. Here is how it works: Given a pattern such as Some follow up to investigate after this PR: Same optimization for patterns with Fixes
|
26d39ee to
c8f3778Comparedanmoseley
commented
Apr 20, 2021
Is this an alternative approach to #42408 ? I need to think about it. |
There are no patterns in our regex perf tests that would be impacted by this optimization. You might consider adding one in the perf repo before committing this, so the change before and after is on the record. In fact, given our limited set of perf tests, it might be a good idea for us to always make sure there's a perf test that would benefit before committing any interesting regex optimization. I suggest in this case several variations. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
| @@ -1217,6 +1233,8 @@ protected override void Go() | |||
| if (len > i && _operator == RegexCode.Notoneloop) | |||
There was a problem hiding this comment.
I wonder whether this should also happen for Notoneloopatomic.
danmoseley
commented
Apr 20, 2021
I would need to spend some time refamiliarizing myself with the code. It would probably be good for @stephentoub to look at it as well as Tanner as he touched it last. |
pgovind
commented
Apr 20, 2021
Not really. It's more of a generalization of #42408 I think. #42408 strictly only optimized a pattern starting with |
pgovind
commented
Apr 20, 2021
If you have the time, I suggest working with this pattern and text: Put a breakpoint at the start of the while loop in |
danmoseley
commented
Apr 20, 2021
There are multiple somewhat related optimizations that concern
And that is what I don't currently have clearly understood in my head right now. 😀 |
(2) and (4) are related. No matter the expression, we start at pos X, find the next place the expression could possibly start, run the match there, and if it fails, bump pos X to be X+1 and try again. That's the bump-ahead mechanism. #42408 optimizes a case where the (3) would be if you had a pattern like (1) doesn't require In other words, these are all mostly orthogonal:
|
danmoseley
commented
Apr 20, 2021
Thanks, that's helpful.
In general, when a match fails, we inevitably bump 1 forward: whereas the bump ahead mechanism as I recall was an optimization (which I believe I proposed, but have paged out) to restart from further than 1 forward. Is this correct: if you have |
stephentoub
commented
Apr 20, 2021
If you're matching against xyabcabcabcabc, you actually need to first try to match starting at the last abc rather than the first, and then if the rest of the pattern can't match there, back up to the next to last abc, and then the next to next to last abc, and so on. But regardless, if you can prove that you can't possibly match starting earlier than X, sure, you can jump to X. #42408 is an example of that for the case where the pattern starts with .*, and you can bump to the next \n rather than +1. In your example, with #42408 you don't even have to try again at y or b, but rather look for the next \n, find it doesn't exist, and you're done. |
Uh oh!
There was an error while loading. Please reload this page.
pgovind
commented
Apr 28, 2021
@stephentoub : I fixed the CI issues. Not super urgent to review this right away. Just making sure it doesn't get lost in your notifications :) |
danmoseley
commented
Apr 28, 2021
@pgovind you'll need to ping him when he's back May 21st if you want his review. Maybe one of us can review before then so that you can merge though. |
pgovind
commented
Apr 28, 2021
Ok, sounds good to me. I'll wait for your sign off then. It's not urgent whatsoever, but I don't want the PR to get too stale either |
jeffhandley
commented
May 21, 2021
@stephentoub If possible once you're back, it'd be great to get your review of this before the Preview 6 snap. |
ccd6643 to
d8e73ccComparepgovind
commented
Jul 14, 2021
Ok, this is done now and I've addressed the last comment @stephentoub |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
stephentoub
commented
Jul 15, 2021
Can you please make sure we have tests that cover various situations here? e.g.
|
pgovind
commented
Jul 16, 2021
Working on the unit tests. Will have them up tomorrow |
ghost
commented
Jul 16, 2021
Hello @pgovind! Because this pull request has the p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me ( |
pgovind
commented
Jul 19, 2021
@danmoseley : Can I get sign off from you to backport this to P7 please? |
jeffhandley
commented
Jul 19, 2021
@pgovind You can request backport approval by doing the following:
|
pgovind
commented
Jul 19, 2021
/backport to release/6.0-preview7 |
Started backporting to release/6.0-preview7: https://github.com/dotnet/runtime/actions/runs/1046477355 |
I spend the last week looking at some potential optimizations in the RegexInterpreter and found this improvement. This PR doesn't change current behavior and is a straightforward optimization. Here is how it works:
Given a pattern such as
.*fooand a text such asabfoocde, theRegexInterpretercurrently sees the.*and zips to the end of the text. Then we start checking forfoofrom the end and backtrack 1 by 1 frometofuntil we see thefooin the text. At this point we stop and return a match. That turns out to be 6 backtracking (and text compare) operations (e, d, c, o, o, f). With this change, after we zip to the end, we useLastIndexOfto find the first potential match in the text and reset our current position toLastIndexOf. If LastIndexOf is -1, we reset to our previous position before we zipped to the end and save all that backtracking work.Required follow up to this PR:
Potential follow up to investigate after this PR:
oneloopandsetloopnodes.Fixes
Optimize .*in #1349Perf numbers on my machine:
There's already ~130 tests with various
.*patterns, so I'm not adding any new ones yet. I'm investigating if there are potentially interesting patterns that are missing from our unit tests, but I'm reasonably confident that we have a good spread already.cc @tannergooding@danmoseley@jeffhandley