2010-12-27 4 views
18

У меня есть большое и сложное регулярное выражение C#, которое работает нормально при интерпретации, но немного медленнее. Я пытаюсь ускорить это, установив RegexOptions.Compiled, и это, кажется, занимает около 30 секунд в первый раз и сразу после этого. Я пытаюсь это отрицать, сначала компилируя регулярное выражение на сборку, поэтому мое приложение может быть как можно быстрее.Почему мое регулярное выражение намного медленнее компилируется, чем интерпретируется?

Моя проблема в том, когда сбор задержка происходит, будь то компилируется в приложении:

Regex myComplexRegex = new Regex(regexText, RegexOptions.Compiled); 
MatchCollection matches = myComplexRegex.Matches(searchText); 
foreach (Match match in matches) // <--- when the one-time long delay kicks in 
{ 

} 

или с помощью Regex.CompileToAssembly заранее:

MatchCollection matches = new CompiledAssembly.ComplexRegex().Matches(searchText); 
foreach (Match match in matches) // <--- when the one-time long delay kicks in 
{ 

} 

Это делает компиляции для сборки в основном бесполезно, поскольку я все еще получаю задержку при первом вызове foreach. Я хочу, чтобы вся компиляция была выполнена во время компиляции (при вызове Regex.CompileToAssembly), а не во время выполнения. Где я иду не так?

(Код, который я использую для компиляции сборки, аналогичен http://www.dijksterhuis.org/regular-expressions-advanced/, если это необходимо).

Edit:

Должен ли я использовать new при вызове скомпилированной сборки в new CompiledAssembly.ComplexRegex().Matches(searchText);? Однако он дает ошибку «ссылка на объект».

Update 2

Спасибо за ответы/комментарии. Регулярное выражение, которое я использую, довольно длинное, но в основном прямолинейное, список тысяч слов, каждый из которых разделен символом |. Я не вижу, чтобы это было проблемой возврата. Строка subject может быть длиной всего одна буква, и она все равно может привести к задержке компиляции. Для регулярного выражения RegexOptions.Compiled это займет более 10 секунд, чтобы выполнить, когда регулярное выражение содержит 5000 слов. Для сравнения, не скомпилированная версия регулярного выражения может принимать 30 000 слов и выполнять их практически мгновенно.

После этого много испытаний на это, что я думаю, что я узнал, это:

  • Не используйте RegexOptions.Compiled, когда ваше регулярное выражение имеет много альтернатив - это может быть очень медленно, чтобы компиляции.
  • .Net будет использовать lazy evaluation for regex, если возможно, и AFAI может видеть, что это также расширяет (по крайней мере до некоторой степени) компиляцию регулярных выражений. Регулярное выражение будет полностью скомпилировано только тогда, когда оно должно быть, и, похоже, не существует способа форсировать компиляцию раньше времени.
  • Regex.CompileToAssembly будет намного полезнее, если бы регулярные выражения могли быть полностью скомпилированы, похоже, что они не имеют никакого смысла.

Пожалуйста, исправьте меня, если я ошибаюсь или что-то не хватает!

+0

Возможно, вам стоит попробовать поделиться фактическим выражением и примерным вводом, который дает вам такое поведение. – driis

+1

Спасибо за сообщение. Имел ту же проблему с некоторым Regex Twitter, перенесенным с Java на .NET. И RegexOptions.Compiled, и .CompileToAssembly заставляли приложение висеть в течение ~ 10 секунд при первом попытке сопоставления. Удалено Regex.Compiled и все мгновенно. – LongZheng

+2

MSDN добавила статью о лучших практиках для .NET 4, которая адресовала [regex's compiled to assemblysies] (http://msdn.microsoft.com/en-us/library/gg578045.aspx#sectionToggle4). –

ответ

2

Попробуйте использовать Regex.CompileToAssembly, а затем ссылку на сборку, чтобы вы могли создавать объекты Regex. RegexOptions.Compiled - это параметр времени выполнения, регулярное выражение все равно будет перекомпилировано каждый раз при запуске приложения.

+0

Это то, что я делаю уже, извините, если это не ясно. Команда CompileToAssembly работает почти мгновенно (я бы подумал, что это займет немного времени), и я получаю задержку в foreach во время выполнения регулярного выражения. – mikel

+3

Не могли бы вы обновить свой вопрос, чтобы показать это? Если вы выполняете 'new Regex', тогда вы создаете новые, не скомпилированные экземпляры Regex. Вам нужно будет использовать классы в своей сборке регулярных выражений. – Douglas

+0

Спасибо Douglas, я обновил его, так что, надеюсь, теперь это немного яснее. – mikel

7

При использовании RegexOptions.Compiled вы должны обязательно повторно использовать объект Regex. Не похоже, что вы это делаете.

RegexOptions.Compiled является компромиссом. Начальная конструкция Regex будет медленнее, потому что код компилируется «на лету», но каждый матч должен быть быстрее. Если ваше регулярное выражение изменяется во время выполнения, вероятно, не будет пользы от использования RegexOptions.Compiled, хотя это может зависеть от фактического выражения.

Update, в комментариях

Если фактический код выглядит так, как вы в курсе, что вы не принимаете какие-либо преимущества CompileToAssembly, как вы создаете новый, на лету скомпилированные экземпляры Regex каждого время, в течение которого выполняется часть кода. Чтобы воспользоваться преимуществами CompileToAssembly, вам нужно сначала скомпилировать Regex; затем возьмите сгенерированную сборку и укажите ее в своем проекте. Затем вы должны создать экземпляр генерируемых, сильно типизированных типов выражений.

В примере, к которому вы ссылаетесь, он имеет регулярное выражение с именем FindTCPIP, которое скомпилируется в тип с именем FindCTPIP. При этом необходимо использовать, необходимо создать новый экземпляр этого конкретного типа, такие как:

TheRegularExpressions.FindTCPIP MatchTCP = new TheRegularExpressions.FindTCPIP(); 
+0

Возможно ли иметь задержку, связанную с первоначальной конструкцией, переданной на время Regex.CompileToAssembly? Несмотря на то, что вся идея CompileToAssembly заключалась в том, чтобы удалить медленность конструкции из среды выполнения во время компиляции, поэтому компромиссов не будет. – mikel

+0

Является ли код отправил в ваш вопрос фактический код, который вы используете? В этом случае CompileToAssembly не будет иметь никакого эффекта, потому что вы каждый раз создаете новые экземпляры Regex. – driis

+0

Простите за то, что я не понимаю, я с тех пор редактировал вопрос. Я попытался создать новые объекты Regex() или создать экземпляр, как в примере, и ни один из них не работает для меня. По-прежнему существует большая задержка при первом запуске регулярного выражения, и это то, что я пытаюсь передать из времени выполнения для компиляции времени. – mikel

2

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

Можете ли вы разместить регулярное выражение и образец ввода там, где он медленный.

Лично мне не нужно было компилировать регулярное выражение, хотя было бы интересно увидеть некоторые фактические цифры о производительности, если вы выбрали этот путь.

+0

Спасибо, я обновил сообщение более подробно. Я не думаю, что это проблема с возвратом, хотя это очень быстро, без флага RegexOptions.Compiled. – mikel

+0

Обратите внимание, что очень легко писать неэффективные регулярные выражения (почти слишком легко). Если регулярное выражение в основном представляет собой набор чередований, мало что можно оптимизировать (но есть место). Будьте осторожны, потому что может вызвать только 1 символ, чтобы вызвать «Катастрофическое обратное отслеживание» http://www.regular-expressions.info/catastrophic.html Меня все еще интересует ваше регулярное выражение и медленные случаи, чтобы взглянуть , – buckley

+0

Также можно отключить обратное отслеживание с помощью элемента языка '(?> Subexpression). Более подробную информацию об этом можно найти в [Рекомендации по регулярным выражениям в .NET Framework] (http://msdn.microsoft.com/en-us/library/gg578045%28v=vs.110%29.aspx# Backtracking) на MSDN. – Ronald

1

Чтобы принудительно инициализировать, вы можете вызвать совпадение с пустой строкой. Кроме того, вы можете использовать ngen для создания собственного образа выражения, чтобы ускорить процесс еще больше. Но, вероятно, самое главное, по сути, так же быстро бросать 30 000 строк. Индексы или инструкции string.Contains или Regex.Match в отношении данного текста, чем компиляция ginormous big expression для сопоставления с одним текстом. Поскольку это требует намного меньше компиляции, джитсинга и т. Д., Поскольку машина состояния намного проще.

Еще одна вещь, которую вы могли бы рассмотреть - это сделать токенизацию текста и пересечь его со списком слов, которые вы используете.

Смежные вопросы