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 одиночных совпадений (создайте регулярное выражение, сопоставьте и утилизируйте)
Type | Platform | Trivial Number | Simple Email Check | Ext Email Check
------------------------------------------------------------------------------
Interpreted | x86 | 4 ms | 26 ms | 31 ms
Interpreted | x64 | 5 ms | 29 ms | 35 ms
Compiled | x86 | 913 ms | 3775 ms | 4487 ms
Compiled | x64 | 3300 ms | 21985 ms | 22793 ms
1 000 000 совпадений - повторное использование объекта Regex
Type | Platform | Trivial Number | Simple Email Check | Ext Email Check
------------------------------------------------------------------------------
Interpreted | x86 | 422 ms | 461 ms | 2122 ms
Interpreted | x64 | 436 ms | 463 ms | 2167 ms
Compiled | x86 | 279 ms | 166 ms | 1268 ms
Compiled | x64 | 281 ms | 176 ms | 1180 ms
Эти результаты показывают, что скомпилированные регулярные выражения могут быть на 60% быстрее для случаев, когда вы повторно используете объект Regex
. Однако в некоторых случаях может быть более на 3 порядка медленнее.
Это также показывает, что x64 версия. 1035 * .NET может быть в 5-6 раз медленнее , когда дело доходит до компиляции регулярных выражений.
Рекомендуется использовать скомпилированную версию в случаях, когда
- Вы не заботитесь о стоимости инициализации объекта и нуждаетесь в дополнительном повышении производительности. (заметьте, мы говорим здесь доли миллисекунды)
- Вы немного заботитесь о стоимости инициализации, но повторно используете объект Regex так много раз, что он компенсирует его в течение жизненного цикла вашего приложения.
гаечный ключ в работах, кеш Regex
Механизм регулярных выражений содержит кэш LRU, в котором хранятся последние 15 регулярных выражений, которые были протестированы с использованием статических методов в классе Regex
.
Например: Regex.Replace
, Regex.Match
и т.д .. все используют кеш Regex.
Размер кэша можно увеличить, установив Regex.CacheSize
. Он принимает изменения в размере в любое время в течение жизненного цикла вашего приложения.
Новые регулярные выражения кэшируются только статическими помощниками в классе Regex. Если вы создаете свои объекты, кэш проверяется (для повторного использования и увеличения), однако, регулярное выражение, которое вы создаете, не добавляется в кеш .
Этот кеш является тривиальным LRU-кешем, он реализован с использованием простого двойного связанного списка. Если вам доведется увеличить его до 5000 и использовать 5000 различных вызовов статических помощников, каждая конструкция регулярного выражения будет сканировать 5000 записей, чтобы увидеть, было ли оно ранее кэшировано. Вокруг чека есть lock , поэтому чек может уменьшить параллелизм и вызвать блокировку потоков.
Число установлено достаточно низким, чтобы защитить себя от подобных случаев, хотя в некоторых случаях у вас может не быть другого выбора, кроме как увеличить его.
Моя сильная рекомендация будет никогда передать параметр RegexOptions.Compiled
статическому помощнику.
Например:
\\ WARNING: bad code
Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled)
Причина в том, что вы сильно рискуете пропустить кеш LRU, что вызовет супердорогую компиляцию. Кроме того, вы понятия не имеете, что делают библиотеки, от которых вы зависите, поэтому не имеете возможности контролировать или прогнозировать наилучший из возможных размер кэша.
Смотрите также: Блог команды BCL
Примечание : это относится к .NET 2.0 и .NET 4.0. В 4.5 ожидаются некоторые изменения, которые могут привести к его пересмотру.