Visitar URL original
Cache the regex used by switch -regex by AkshayDhola · Pull Request #27977 · PowerShell/PowerShell · GitHub
Skip to content

Cache the regex used by switch -regex - #27977

Open
Akshay (AkshayDhola) wants to merge 5 commits into
PowerShell:masterfrom
AkshayDhola:fix-switch-regex-cache
Open

Akshay (AkshayDhola) wants to merge 5 commits into
PowerShell:masterfrom
AkshayDhola:fix-switch-regex-cache

Conversation

@AkshayDhola

@AkshayDhola Akshay (AkshayDhola) commented Sep 4, 2026 •

Copy link
Copy Markdown

PR Summary

switch -regex no longer rebuilds a Regex object for every line that matches, making
regex switch statements substantially faster and much lighter on allocation.

SwitchOps.ConditionSatisfiedRegex paired a static Regex.Match call with new Regex(...)
so it could read the group names, on the assumption — stated in the code comment — that the
constructor would hit .NET's regex cache. Only the static Regex methods consult that cache,
so every successful match paid a full pattern parse and matcher codegen. The guard did not
help either, since m.Groups.Count is at least 1 for any successful match.

This routes the branch through ParserOps.NewRegex, the cache that -match, -replace and
-split already use, and matches on that instance. The pattern and RegexOptions are
unchanged, so matching semantics are identical, and the existing catch (ArgumentException)
still fires for invalid patterns.

Also adds test/powershell/Language/Scripting/SwitchRegex.Tests.ps1. switch -regex had no
test coverage anywhere under test/powershell.

Fix #27975

PR Context

Reported in #27975. Measured on a local Release build of 6ca24ccf0, 200,000-line log,
median of 5 runs, both binaries built the same way:

Scenario Before After Change
1 clause, every line matches 1144.0 ms 305.3 ms 3.7x
24 clauses, past [regex]::CacheSize 21764.1 ms 858.8 ms 25.3x
Allocation, 1 clause 1008.6 MB 250.3 MB -75%

The second row is a separate effect: because the old code went through the static cache,
which holds only Regex.CacheSize (15) patterns, a switch with more distinct clause patterns
than that recompiled every clause for every line. Allocation is the most stable signal — it is
identical at 1008.6 MB on both the unpatched local build and the shipped 7.6.5 release.

One design point worth a reviewer's attention, also raised on the issue: clause patterns are
now retained in a process-wide cache of up to 1000 entries rather than the static cache's 15,
and [regex]::CacheSize no longer influences switch -regex. That makes switch consistent
with the other regex operators, but it is a policy change rather than a pure optimization, so
I would rather flag it than have it found in review.

Verification: the new test file covers named and numbered groups, the exact $matches key
set, $matches left untouched on a non-match, default case-insensitivity, -CaseSensitive, a
[regex] instance as the clause condition under both switch modes, clause fallthrough, the
InvalidRegularExpression error id, switch -regex -file, and 24 distinct clause patterns
over repeated passes. It passes on both the patched and unpatched binary, since it asserts
behavior rather than speed. test/powershell/Language and test/powershell/engine (6764
tests) show no regressions against an unpatched baseline build of the same commit.

The condition as Regex fast path in the same method is deliberately left untouched, so this
does not interact with #8946.

PR Checklist

Copilot AI lite review requested due to automatic review settings September 4, 2026 18:28
@AkshayDhola
Akshay (AkshayDhola) requested a review from a team as a code owner September 4, 2026 18:28
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new test assigns to the automatic variable $matches, which is discouraged and trips PSScriptAnalyzer (PSAvoidAssignmentToAutomaticVariable), so the test should be adjusted to avoid direct assignment.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves switch -regex performance by reusing cached Regex instances (via PowerShell’s existing ParserOps.NewRegex cache) instead of constructing a new Regex on every successful match, and adds targeted Pester coverage for switch -regex behavior.

Changes:

  • Route switch -regex matching through ParserOps.NewRegex(...) and match on the cached instance to avoid repeated regex recompilation/allocation.
  • Add new Pester tests covering $matches population semantics, case-sensitivity behavior, clause fallthrough, malformed-pattern error id, -file mode, and multi-clause scenarios beyond [regex]::CacheSize.
File summaries
File Description
src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs Updates regex switch matching to use the engine regex cache and avoid per-match Regex construction.
test/powershell/Language/Scripting/SwitchRegex.Tests.ps1 Adds comprehensive behavioral tests for switch -regex, including $matches behavior and -file operation.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread test/powershell/Language/Scripting/SwitchRegex.Tests.ps1 Outdated
@AkshayDhola
Akshay (AkshayDhola) marked this pull request as draft September 4, 2026 18:35
@AkshayDhola

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@AkshayDhola
Akshay (AkshayDhola) marked this pull request as ready for review September 5, 2026 05:08
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Comment thread src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs Outdated
Comment thread src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs Outdated
Comment thread src/System.Management.Automation/engine/runtime/Operations/MiscOps.cs Outdated
@iSazonov Ilya (iSazonov) added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label Sep 8, 2026
@iSazonov

Copy link
Copy Markdown
Collaborator

Akshay (@AkshayDhola) Please rebase to pass CIs.

@iSazonov Ilya (iSazonov) added CL-Performance Indicates that a PR should be marked as a performance improvement in the Change Log and removed CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log labels Sep 11, 2026
@AkshayDhola
Akshay (AkshayDhola) force-pushed the fix-switch-regex-cache branch 2 times, most recently from 968e2dd to aaf9270 Compare September 12, 2026 08:36
@microsoft-github-policy-service microsoft-github-policy-service Bot added the Review - Needed The PR is being reviewed label Sep 26, 2026
@microsoft-github-policy-service

Copy link
Copy Markdown
Contributor

This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days.
Maintainer, please provide feedback and/or mark it as Waiting on Author

AkshayDhola added 5 commits October 5, 2026 06:40
SwitchOps.ConditionSatisfiedRegex paired a static Regex.Match call with
new Regex(...) so it could read the group names, on the assumption that
the constructor would hit .NET's regex cache. Only the static Regex
methods consult that cache, so every successful match paid a full
pattern parse and matcher codegen. The guard did not help either, since
m.Groups.Count is at least 1 for any successful match.

Route the branch through ParserOps.NewRegex instead, the cache that
-match, -replace and -split already use, and match on that instance.
The pattern and RegexOptions are unchanged, so matching semantics are
identical.

Clause patterns are now retained in a cache of up to 1000 entries rather
than the static cache's 15, which makes switch consistent with the other
regex operators, and [regex]::CacheSize no longer affects switch -regex.

Add SwitchRegex.Tests.ps1; switch -regex had no test coverage.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CL-Performance Indicates that a PR should be marked as a performance improvement in the Change Log Review - Needed The PR is being reviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

switch -regex recompiles the clause pattern on every successful match

6 participants