Компилируем регулярные выражения или нет?

в 21:52, , рубрики: regex, regexp, regular expressions, Регулярные выражения, метки: , ,

В книге Дж.Фридла «Регулярные выражения» я наткнулся на то, что автор намекает не использовать компиляцию регулярных выражений для повседневных случаев и использовать только для критических, когда крайне важна скорость выполнения, особенно если выражение работает с большим объемом текста.

Аналогичное мнение наблюдается и в msdn.
Но так ли это?

Немного теории

Под термином «компиляция» понималась операция, выполняемая перед применением регулярного выражения, в ходе которой система проверяет синтаксис выражения и преобразует его во
внутреннюю форму, пригодную для применения к тексту. В .NET этот
процесс называется parsing, а две разновидности «компиляции» относятся к разным оптимизациям, выполняемым на стадии разбора.

Ниже приводится подробное описание порядка оптимизации:
• Разбор. Когда регулярное выражение впервые встречается в процессе выполнения программы, оно проверяется и преобразуется во внутреннюю форму, используемую механизмом регулярных выражений.
• Оперативная компиляция. Параметр RegexOptions.Compiled входитв число параметров, используемых при построении регулярных выражений. Он означает, что механизм регулярных выражений не ограничивается простым преобразованием выражения в стандартную внутреннюю форму, но компилирует его в низкоуровневый код MSIL, который, в свою очередь, преобразуется в еще более быстрый машинный код JIT компилятором при фактическом применении регулярного выражения.
• Предварительная компиляция регулярных выражений. Объект (или объекты) Regex может быть преобразован в сборку, записанную на диск в виде DLL библиотеки. В дальнейшем содержимое библиотеки может использоваться другими программами — это называется «компиляцией сборки».

В книге приводится таблица, где сравнивается скорость выполнения с параметром и без него.
image

Перейдем к проверке?

Когда у меня возникают сомнения, я стараюсь проверять. Сейчас мы напишем небольшую программу, в которой мы заполним строку одним миллионом целых чисел, а затем, используя не сложное регулярное выражение, проверим, как отразится использование «компиляции» на времени выполнения программы в целом, а так же на самом поиске выражения.

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-раз).

image

Я предположил, что строка с цифрами слишком простая для регулярного выражения, которое просто берёт 2 цифры. Для чистоты эксперимента, я написал генератор случайной последовательности букв английского алфавита между генерацией случайной цифры. Как я и ожидал, время выполнения программы существенно выросло, так же выросло и время поиска.
Но, с «компиляцией» общее время выполнения и время поиска всегда было ниже, чем без нее.

По результатам, можно говорить о том, что использование RegexOptions.Compiled не приводит к увеличению времени запуска, как об этом говорят. Почему же утверждают обратное?

Ps. Кто из вас использует/не использует компиляцию и почему?

Автор: NightSoul

Поделиться

* - обязательные к заполнению поля