为什么我的正则表达式编译得比解释慢得多?

我有一个庞大而复杂的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/ ,如果这是相关的)。

编辑:

new CompiledAssembly.ComplexRegex().Matches(searchText);调用已编译的程序集时,我应该使用new new CompiledAssembly.ComplexRegex().Matches(searchText); ? 它虽然没有它,但它提供了“需要对象引用”错误。

更新2

谢谢你的回答/评论。 我正在使用的正则表达式很长但基本上很简单,列出了数千个单词,每个单词用|分隔。 我真的看不出这是一个回溯问题。 主题字符串可以只有一个字母长,它仍然可以导致编译延迟。 对于RegexOptions.Compiled正则表达式,当正则表达式包含5000个单词时,执行将需要10秒以上。 为了比较,正则表达式的非编译版本可以使用30,000多个单词,并且仍然可以立即执行。

在对此进行了大量测试之后,我认为我发现的是:

  • 当你的正则表达式有很多选择时,不要使用RegexOptions.Compiled – 它的编译速度可能非常慢。
  • .Net会在可能的情况下对正则表达式使用延迟求值 ,AFAI也可以看到这也扩展(至少在某种程度上)到正则表达式编译。 一个正则表达式只有在必须时才会被完全编译,并且似乎无法提前强制编译。
  • 如果可以强制完全编译正则表达式,那么Regex.CompileToAssembly会更有用,它似乎正在接近无意义。

如果我错了或错过了什么,请纠正我!

使用RegexOptions.Compiled ,应确保重新使用Regex对象。 看起来你好像不这样做。

RegexOptions.Compiled是一种权衡。 正则表达式的初始构造将更慢,因为代码是即时编译的,但每个匹配应该更快。 如果正则表达式在运行时更改,则使用RegexOptions.Compiled可能没有任何好处,尽管它可能取决于所涉及的实际表达式。

根据评论更新

如果您的实际代码与您发布的代码类似,那么您不会利用CompileToAssembly ,因为每次运行该代码时都会创建新的,正在运行的Regex实例。 为了利用CompileToAssembly,您需要先编译Regex; 然后获取生成的程序集并在项目中引用它。 然后,您应该实例化生成的生成的强类型Regex类型。

在您链接到的示例中,他有一个名为FindTCPIP的正则表达式,它被编译为名为FindCTPIP的类型。 当需要使用时,应该创建此特定类型的新实例,例如:

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

尝试使用Regex.CompileToAssembly ,然后链接到程序集,以便您可以构造Regex对象。 RegexOptions.Compiled是一个运行时选项,每次运行应用程序时仍然会重新编译正则表达式。

调查缓慢的正则表达式时,一个非常可能的原因是它回溯太多。 这可以通过重写正则表达式来解决,以便回溯的数量不存在或最小。

你可以发布正则表达式和一个缓慢的样本输入。

就个人而言,我没有必要编译一个正则表达式,尽管如果你采用这条路径,有趣的是看一些关于性能的实际数字。

要强制初始化,您可以针对空字符串调用Match。 最重要的是,您可以使用ngen创建表达式的本机图像,以进一步加快进程。 但也许最重要的是,对于给定的文本,抛出30.000 string.IndexOf或string.Contains或Regex.Match语句基本上就像编译一个巨大的表达式来匹配单个文本一样快。 因为这需要更少的编译,jitting等,因为状态机更简单。

您可以考虑的另一件事是标记文本并将其与您所追求的单词列表相交。