RegexOptions.Compiled
поручает механизму регулярных выражений компилировать выражение регулярного выражения в IL с использованием облегченной генерации кода ( LCG ). Эта компиляция происходит во время строительства объекта и сильно замедляет его. В свою очередь совпадения с использованием регулярного выражения выполняются быстрее.
Если вы не укажете этот флаг, ваше регулярное выражение будет считаться «интерпретированным».
Возьмите этот пример:
public static void TimeAction(string description, int times, Action func)
{
// warmup
func();
var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < times; i++)
{
func();
}
watch.Stop();
Console.Write(description);
Console.WriteLine(" Time Elapsed {0} ms", watch.ElapsedMilliseconds);
}
static void Main(string[] args)
{
var simple = "^\\d+$";
var medium = @"^((to|from)\W)?(?<url>http://[\w\.:]+)/questions/(?<questionId>\d+)(/(\w|-)*)?(/(?<answerId>\d+))?";
var complex = @"^(([^<>()[\]\\.,;:\s@""]+"
+ @"(\.[^<>()[\]\\.,;:\s@""]+)*)|("".+""))@"
+ @"((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
+ @"\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+"
+ @"[a-zA-Z]{2,}))$";
string[] numbers = new string[] {"1","two", "8378373", "38737", "3873783z"};
string[] emails = new string[] { "sam@sam.com", "sss@s", "sjg@ddd.com.au.au", "onelongemail@oneverylongemail.com" };
foreach (var item in new[] {
new {Pattern = simple, Matches = numbers, Name = "Simple number match"},
new {Pattern = medium, Matches = emails, Name = "Simple email match"},
new {Pattern = complex, Matches = emails, Name = "Complex email match"}
})
{
int i = 0;
Regex regex;
TimeAction(item.Name + " interpreted uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
i = 0;
TimeAction(item.Name + " compiled uncached single match (x1000)", 1000, () =>
{
regex = new Regex(item.Pattern, RegexOptions.Compiled);
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern);
i = 0;
TimeAction(item.Name + " prepared interpreted match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
regex = new Regex(item.Pattern, RegexOptions.Compiled);
i = 0;
TimeAction(item.Name + " prepared compiled match (x1000000)", 1000000, () =>
{
regex.Match(item.Matches[i++ % item.Matches.Length]);
});
}
}
Он выполняет 4 теста по 3 различным регулярным выражениям. Сначала он проверяет одного однажды от матча (скомпилированный против не компилируется). Во-вторых, он проверяет повторяющиеся совпадения, которые используют одно и то же регулярное выражение.
Результаты на моей машине (скомпилированы в выпуске, без отладчика)
1000 одиночных матчей (построить Regex, Match и dispose)
Тип | Платформа | Тривиальный номер | Простая проверка электронной почты | Ext Email Check
-------------------------------------------------- ----------------------------
Интерпретированный | х86 | 4 мс | 26 мс | 31 мс
Интерпретированный | х64 | 5 мс | 29 мс | 35 мс
Скомпилировано | х86 | 913 мс | 3775 мс | 4487 мс
Скомпилировано | х64 | 3300 мс | 21985 мс | 22793 мс
1 000 000 совпадений - повторное использование объекта Regex
Тип | Платформа | Тривиальный номер | Простая проверка электронной почты | Ext Email Check
-------------------------------------------------- ----------------------------
Интерпретированный | х86 | 422 мс | 461 мс | 2122 мс
Интерпретированный | х64 | 436 мс | 463 мс | 2167 мс
Скомпилировано | х86 | 279 мс | 166 мс | 1268 мс
Скомпилировано | х64 | 281 мс | 176 мс | 1180 мс
Эти результаты показывают, что скомпилированные регулярные выражения могут быть на 60% быстрее в случаях, когда вы повторно используете Regex
объект. Однако в некоторых случаях его можно построить на 3 порядка медленнее.
Это также показывает, что x64-версия .NET может быть в 5-6 раз медленнее, когда речь идет о компиляции регулярных выражений.
Рекомендуется использовать скомпилированную версию в случаях, когда
- Вы не заботитесь о стоимости инициализации объекта и нуждаетесь в дополнительном повышении производительности. (обратите внимание, мы говорим здесь доли миллисекунды)
- Вы немного заботитесь о стоимости инициализации, но повторно используете объект Regex так много раз, что он компенсирует его в течение жизненного цикла вашего приложения.
Гаечный ключ в работах, кеш Regex
Механизм регулярных выражений содержит кэш LRU, который содержит последние 15 регулярных выражений, которые были протестированы с использованием статических методов Regex
класса.
Например: Regex.Replace
и Regex.Match
т. Д. Все используют кэш Regex.
Размер кеша можно увеличить настройкой Regex.CacheSize
. Он принимает изменения в размере в любое время в течение жизненного цикла вашего приложения.
Новые регулярные выражения кэшируются только статическими помощниками в классе Regex. Если вы создаете свои объекты, кеш проверяется (для повторного использования и увеличения), однако создаваемое вами регулярное выражение не добавляется в кеш .
Этот кеш является тривиальным LRU-кешем, он реализован с использованием простого двойного связанного списка. Если вам доведется увеличить его до 5000 и использовать 5000 различных вызовов статических помощников, каждая конструкция регулярного выражения будет сканировать 5000 записей, чтобы увидеть, было ли оно ранее кэшировано. Вокруг проверки есть блокировка , поэтому проверка может уменьшить параллелизм и ввести блокировку потоков.
Число установлено достаточно низким, чтобы защитить себя от подобных случаев, хотя в некоторых случаях у вас может не быть иного выбора, кроме как увеличить его.
Моя настоятельная рекомендация не будет никогда передать RegexOptions.Compiled
опцию статического помощника.
Например:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
Причина в том, что вы сильно рискуете пропустить кеш LRU, что приведет к очень дорогой компиляции. Кроме того, вы понятия не имеете, что делают библиотеки, от которых вы зависите, поэтому не имеете возможности контролировать или прогнозировать наилучший возможный размер кэша.
Смотрите также: блог команды BCL
Примечание : это актуально для .NET 2.0 и .NET 4.0. В 4.5 ожидаются некоторые изменения, которые могут привести к его пересмотру.