From 54edaee83fda7090aa0aa6200e65766cda3775a3 Mon Sep 17 00:00:00 2001 From: Ilya Date: Fri, 1 Nov 2019 23:14:31 +0500 Subject: [PATCH] Improve the caching when creating new `Regex` instances (#10657) --- .../engine/lang/parserutils.cs | 43 ++++++++++--------- .../engine/regex.cs | 4 +- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/src/System.Management.Automation/engine/lang/parserutils.cs b/src/System.Management.Automation/engine/lang/parserutils.cs index 60d4711da3..e629a427b7 100644 --- a/src/System.Management.Automation/engine/lang/parserutils.cs +++ b/src/System.Management.Automation/engine/lang/parserutils.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; @@ -9,10 +10,8 @@ using System.Management.Automation.Internal; using System.Management.Automation.Internal.Host; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; -using System.Text; using System.Text.RegularExpressions; using Dbg = System.Management.Automation.Diagnostics; @@ -1347,35 +1346,37 @@ namespace System.Management.Automation } /// - /// Cache regular expressions... + /// Cache regular expressions. /// /// The string to find the pattern for. - /// The options used to create the regex... - /// A case-insensitive Regex... + /// The options used to create the regex. + /// New or cached Regex. internal static Regex NewRegex(string patternString, RegexOptions options) { - if (options != RegexOptions.IgnoreCase) - return new Regex(patternString, options); - - lock (s_regexCache) + var subordinateRegexCache = s_regexCache.GetOrAdd(options, s_subordinateRegexCacheCreationDelegate); + if (subordinateRegexCache.TryGetValue(patternString, out Regex result)) { - Regex result; - if (s_regexCache.TryGetValue(patternString, out result)) + return result; + } + else + { + if (subordinateRegexCache.Count > MaxRegexCache) { - return result; - } - else - { - if (s_regexCache.Count > MaxRegexCache) - s_regexCache.Clear(); - Regex re = new Regex(patternString, RegexOptions.IgnoreCase); - s_regexCache.Add(patternString, re); - return re; + // TODO: it would be useful to get a notice (in telemetry?) if the cache is full. + subordinateRegexCache.Clear(); } + + var regex = new Regex(patternString, options); + return subordinateRegexCache.GetOrAdd(patternString, regex); } } - private static Dictionary s_regexCache = new Dictionary(); + private static readonly ConcurrentDictionary> s_regexCache = + new ConcurrentDictionary>(); + + private static readonly Func> s_subordinateRegexCacheCreationDelegate = + key => new ConcurrentDictionary(StringComparer.Ordinal); + private const int MaxRegexCache = 1000; /// diff --git a/src/System.Management.Automation/engine/regex.cs b/src/System.Management.Automation/engine/regex.cs index 71c6c8bac8..2c4ade7cb5 100644 --- a/src/System.Management.Automation/engine/regex.cs +++ b/src/System.Management.Automation/engine/regex.cs @@ -909,7 +909,7 @@ namespace System.Management.Automation WildcardPatternParser.Parse(wildcardPattern, parser); try { - return new Regex(parser._regexPattern.ToString(), parser._regexOptions); + return ParserOps.NewRegex(parser._regexPattern.ToString(), parser._regexOptions); } catch (ArgumentException) { @@ -1258,7 +1258,7 @@ namespace System.Management.Automation protected override void EndBracketExpression() { _bracketExpressionBuilder.Append(']'); - Regex regex = new Regex(_bracketExpressionBuilder.ToString(), _regexOptions); + Regex regex = ParserOps.NewRegex(_bracketExpressionBuilder.ToString(), _regexOptions); _patternElements.Add(new BracketExpressionElement(regex)); } }