Improve the caching when creating new Regex instances (#10657)

This commit is contained in:
Ilya
2019-11-01 11:14:31 -07:00
committed by Dongbo Wang
parent 4b9c2b2008
commit 54edaee83f
2 changed files with 24 additions and 23 deletions
@@ -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
}
/// <summary>
/// Cache regular expressions...
/// Cache regular expressions.
/// </summary>
/// <param name="patternString">The string to find the pattern for.</param>
/// <param name="options">The options used to create the regex...</param>
/// <returns>A case-insensitive Regex...</returns>
/// <param name="options">The options used to create the regex.</param>
/// <returns>New or cached Regex.</returns>
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<string, Regex> s_regexCache = new Dictionary<string, Regex>();
private static readonly ConcurrentDictionary<RegexOptions, ConcurrentDictionary<string, Regex>> s_regexCache =
new ConcurrentDictionary<RegexOptions, ConcurrentDictionary<string, Regex>>();
private static readonly Func<RegexOptions, ConcurrentDictionary<string, Regex>> s_subordinateRegexCacheCreationDelegate =
key => new ConcurrentDictionary<string, Regex>(StringComparer.Ordinal);
private const int MaxRegexCache = 1000;
/// <summary>
@@ -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));
}
}