В книге Дж.Фридла «Регулярные выражения» я наткнулся на то, что автор намекает не использовать компиляцию регулярных выражений для повседневных случаев и использовать только для критических, когда крайне важна скорость выполнения, особенно если выражение работает с большим объемом текста.
Аналогичное мнение наблюдается и в msdn.
Но так ли это?
Немного теории
Под термином «компиляция» понималась операция, выполняемая перед применением регулярного выражения, в ходе которой система проверяет синтаксис выражения и преобразует его во
внутреннюю форму, пригодную для применения к тексту. В .NET этот
процесс называется parsing, а две разновидности «компиляции» относятся к разным оптимизациям, выполняемым на стадии разбора.
Ниже приводится подробное описание порядка оптимизации:
• Разбор. Когда регулярное выражение впервые встречается в процессе выполнения программы, оно проверяется и преобразуется во внутреннюю форму, используемую механизмом регулярных выражений.
• Оперативная компиляция. Параметр RegexOptions.Compiled входитв число параметров, используемых при построении регулярных выражений. Он означает, что механизм регулярных выражений не ограничивается простым преобразованием выражения в стандартную внутреннюю форму, но компилирует его в низкоуровневый код MSIL, который, в свою очередь, преобразуется в еще более быстрый машинный код JIT компилятором при фактическом применении регулярного выражения.
• Предварительная компиляция регулярных выражений. Объект (или объекты) Regex может быть преобразован в сборку, записанную на диск в виде DLL библиотеки. В дальнейшем содержимое библиотеки может использоваться другими программами — это называется «компиляцией сборки».
В книге приводится таблица, где сравнивается скорость выполнения с параметром и без него.
Перейдем к проверке?
Когда у меня возникают сомнения, я стараюсь проверять. Сейчас мы напишем небольшую программу, в которой мы заполним строку одним миллионом целых чисел, а затем, используя не сложное регулярное выражение, проверим, как отразится использование «компиляции» на времени выполнения программы в целом, а так же на самом поиске выражения.
Stopwatch howLongAll = new Stopwatch();
howLongAll.Start();
Random random = new Random();
string line = "";
while (line.Length<1000000)
{
line += random.Next(9999999);
}
string pattern = @"[0-9]{1,2}";// с помощью этого паттерна мы будем находить два числа
Regex regex = new Regex(pattern,RegexOptions.Compiled); //Опция компиляции включена
Match match = regex.Match(line);
int count = 0;
Stopwatch howLong = new Stopwatch();
howLong.Start();
while (match.Value!="")
{
line = line.Replace(match.Value, ""); //заменяем найденное значение в строке на ""
match = match.NextMatch(); // ищем следующий элемент
if (match.Value!= "")
{
count++;
}
}
howLong.Stop();
howLongAll.Stop();
Console.WriteLine("Время = "+howLong.ElapsedMilliseconds +
", количество совпадений = " + count + " Общее время выполнения = "+howLongAll.ElapsedMilliseconds/1000 );
Результат
Результат который я получил является усреднённым (программа запускалась с параметром и без n-раз).
Я предположил, что строка с цифрами слишком простая для регулярного выражения, которое просто берёт 2 цифры. Для чистоты эксперимента, я написал генератор случайной последовательности букв английского алфавита между генерацией случайной цифры. Как я и ожидал, время выполнения программы существенно выросло, так же выросло и время поиска.
Но, с «компиляцией» общее время выполнения и время поиска всегда было ниже, чем без нее.
По результатам, можно говорить о том, что использование RegexOptions.Compiled не приводит к увеличению времени запуска, как об этом говорят. Почему же утверждают обратное?
Ps. Кто из вас использует/не использует компиляцию и почему?
Автор: NightSoul