Как работает RegexOptions.Compiled?

Что происходит за кулисами, когда вы отмечаете регулярное выражение как скомпилированное? Как это сравнивается / отличается от кэшированного регулярного выражения?

Используя эту информацию, как вы определяете, когда стоимость вычислений пренебрежимо мала по сравнению с увеличением производительности?

RegexOptions.Compiled инструктирует 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)?(?http://[\w\.:]+)/questions/(?\d+)(/(\w|-)*)?(/(?\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[] { "[email protected]", "[email protected]", "[email protected]", "[email protected]" }; 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 различным регулярным выражениям. Сначала он тестирует однократное совпадение (скомпилировано vs non compiled). Во-вторых, он проверяет повторяющиеся совпадения, которые повторно используют одно и то же регулярное выражение.

Результаты на моей машине (скомпилированные в выпуске, без отладчика)

1000 одиночных совпадений (построить Regex, Match and dispose)

 Тип |  Платформа |  Тривиальный номер |  Простая проверка электронной почты |  Экзамен по электронной почте
 -------------------------------------------------- ----------------------------
 Интерпретированный |  x86 |  4 мс |  26 ms |  31 мс
 Интерпретированный |  x64 |  5 мс |  29 ms |  35 мс
 Скомпилированный |  x86 |  913 мс |  3775 мс |  4487 мс
 Скомпилированный |  x64 |  3300 мс |  21985 мс |  22793 мс

1,000,000 совпадений – повторное использование объекта Regex

 Тип |  Платформа |  Тривиальный номер |  Простая проверка электронной почты |  Экзамен по электронной почте
 -------------------------------------------------- ----------------------------
 Интерпретированный |  x86 |  422 мс |  461 мс |  2122 мс
 Интерпретированный |  x64 |  436 мс |  463 мс |  2167 мс
 Скомпилированный |  x86 |  279 мс |  166 мс |  1268 мс
 Скомпилированный |  x64 |  281 мс |  176 мс |  1180 мс

Эти результаты показывают, что скомпилированные регулярные выражения могут быть на 60% быстрее для случаев, когда вы повторно используете объект Regex . Однако в некоторых случаях может быть на 3 порядка медленнее строить.

Он также показывает, что x64-версия .NET может быть в 5-6 раз медленнее, когда дело доходит до компиляции регулярных выражений.


Рекомендация заключалась бы в использовании скомпилированной версии в случаях, когда

  1. Вы не заботитесь об стоимости инициализации объекта и нуждаетесь в дополнительном повышении производительности. (обратите внимание, что мы говорим о долях миллисекунды здесь)
  2. Вы немного заботитесь об стоимости инициализации, но повторно используете объект Regex столько раз, что это компенсирует его во время жизненного цикла приложения.

Ключ в работе, кэш Regex

Механизм регулярных выражений содержит кеш LRU, который содержит последние 15 регулярных выражений, которые были протестированы с использованием статических методов в classе Regex .

Например: Regex.Replace , Regex.Match т. Д. Все используют кэш Regex.

Размер кеша можно увеличить, установив Regex.CacheSize . Он принимает изменения в размере в любое время в течение жизненного цикла вашего приложения.

Новые регулярные выражения кэшируются только статическими помощниками в classе Regex. Если вы создаете свои объекты, кеш проверяется (для повторного использования и наложения), однако, созданное вами регулярное выражение не добавляется к кешу .

Этот кеш представляет собой тривиальный кеш LRU, он реализуется с использованием простого двойного списка. Если вам удастся увеличить его до 5000 и использовать 5000 разных вызовов для статических помощников, каждая конструкция регулярных выражений будет обходить 5000 записей, чтобы увидеть, было ли они ранее кэшированы. Существует блокировка вокруг проверки, поэтому проверка может уменьшить параллельность и ввести блокировку streamа.

Количество установлено достаточно низко, чтобы защитить себя от таких случаев, хотя в некоторых случаях у вас может не быть выбора, кроме как увеличить его.

Моя сильная рекомендация никогда не будет передавать параметр RegexOptions.Compiled статическому помощнику.

Например:

 \\ WARNING: bad code Regex.IsMatch("10000", @"\\d+", RegexOptions.Compiled) 

Причина в том, что вы сильно рискуете пропустить кеш LRU, который вызовет супервысокую компиляцию. Кроме того, вы понятия не имеете, от каких библиотек вы зависите, поэтому не имеете возможности контролировать или outlookировать максимально возможный размер кеша.

Смотрите также: блог группы BCL


Примечание : это относится к .NET 2.0 и .NET 4.0. Есть некоторые ожидаемые изменения в 4.5, которые могут привести к его пересмотру.

Эта запись в блоге Team BCL дает хороший обзор: « Регулярное выражение ».

Короче говоря, существует три типа регулярных выражений (каждый из которых выполняется быстрее, чем предыдущий):

  1. интерпретированы

    быстро создавать «на лету», медленно выполнять

  2. скомпилированный (тот, о котором вы, кажется, спрашиваете)

    медленнее создавать «на лету», быстро выполнять (полезно для выполнения в цикле)

  3. прекомпилируются

    создавать во время компиляции вашего приложения (без штрафа за создание времени), быстро выполнить

Итак, если вы собираетесь выполнять регулярное выражение только один раз или в критическом для вас разделе вашего приложения (например, подтверждение ввода пользователя), вы в порядке с опцией 1.

Если вы собираетесь запускать регулярное выражение в цикле (то есть поэтапный parsing файла), вы должны пойти с опцией 2.

Если у вас много регулярных выражений, которые никогда не будут изменяться для вашего приложения и будут интенсивно использоваться, вы можете пойти с опцией 3.

Следует отметить, что производительность регулярных выражений с .NET 2.0 была улучшена с помощью кэша MRU для несжатых регулярных выражений. Код библиотеки Regex больше не переинтерпретирует одно и то же не скомпилированное регулярное выражение каждый раз.

Таким образом, существует потенциально более высокая производительность при компиляции и регулярном выражении на лету. В дополнение к более медленным временам загрузки система также использует больше памяти для компиляции регулярного выражения для кодов операций.

По существу, текущий совет либо не компилирует регулярное выражение, либо заранее компилирует его в отдельную сборку.

Ссылка: BCL Team Blog Регулярное выражение (David Gutierrez)

1) Команда библиотеки базового classа по скомпилированному регулярному выражению

2) Ужас кодирования, ссылающийся на № 1 с некоторыми хорошими моментами на компромиссы

  • Bash, grep между двумя строками с заданной строкой
  • Regex - не содержит определенных символов
  • Образец ввода HTML5 для цитаты
  • Windows: копировать / перемещать файлы с регулярными выражениями имен файлов?
  • Как заменить все строки на числа, содержащиеся в каждой строке в Notepad ++?
  • В чем разница между «группами» и «захватами» в регулярных выражениях .NET?
  • Сравните одну строку с несколькими значениями в одном выражении
  • Как я могу использовать специальные символы, такие как \ ^ $.? * | + () [{В моем регулярном выражении?
  • Регулярные выражения в выражении случая Бэша
  • Удаление всех неглавных символов с помощью регулярных выражений (регулярные выражения в шаблоне регулярных выражений C #)
  • Регулярные выражения с повторяющимися символами
  • Давайте будем гением компьютера.