正则表达式性能下降
Posted
技术标签:
【中文标题】正则表达式性能下降【英文标题】:regex performance degrades 【发布时间】:2013-02-11 17:17:43 【问题描述】:我正在编写一个 C# 应用程序,它在很多(约 2500 万)个字符串上运行多个正则表达式(约 10 个)。我确实尝试用谷歌搜索这个,但是任何搜索“减慢”的正则表达式都充满了关于反向引用等如何减慢正则表达式的教程。我假设这不是我的问题,因为我的正则表达式开始快而慢。
对于前一百万个左右的字符串,每 1000 个字符串大约需要 60 毫秒来运行正则表达式。到最后,它的速度减慢到大约需要 600 毫秒。有谁知道为什么?
情况更糟,但我通过使用 RegEx
的实例而不是缓存版本并尽可能地编译表达式来改进它。
我的一些正则表达式需要改变,例如取决于用户的名字,它可能是
mike said (\w*)
或 john said (\w*)
我的理解是无法编译那些正则表达式并传入参数(例如saidRegex.Match(inputString, userName)
)。
有人有什么建议吗?
[编辑以准确反映速度 - 每 1000 个字符串,而不是每字符串]
【问题讨论】:
你能贴一些代码吗? 如果您采取了通常的措施来提高性能,很难提供更多建议。如果没问题,你能展示你的正则表达式吗? 正则表达式引擎本身不太可能变慢。更有可能是您的应用程序正在保存结果,因此内存在增长,这会导致整体性能下降。监控您的进程内存大小。还要检查内存泄漏。 另外,您如何确定 RegEx 本身是缓慢的根源?您是否在循环中做了其他可能也应受到指责的事情,例如检索“当前”字符串等? 我正在检索当前字符串,这让我很生气。我不再这样做了。我正在存储结果,但是当我运行性能分析器时,它仍然将正则表达式归咎于最严重的违规者。给我一点时间,我将发布一些删除识别内容的正则表达式。感谢您的快速回复! 【参考方案1】:这可能不是您关于 RegEx 性能下降问题的直接答案 - 这有点令人着迷。但是 - 在阅读了上面的所有评论和讨论之后 - 我会建议以下内容:
解析一次数据,将匹配的数据拆分到数据库表中。您似乎正在尝试捕获以下字段:
Player_Name | Monetary_Value
如果您要创建每行包含这些值的数据库表,然后在创建时捕获每个新行 - 解析它 - 并附加到数据表 - 您可以轻松地进行任何类型的分析/计算针对数据 - 无需一次又一次地解析 25M 行(这是一种浪费)。
另外 - 在第一次运行时,如果您要将 25M 记录分解为 100,000 个记录块,然后运行算法 250 次(100,000 x 250 = 25,000,000) - 您可以享受您所描述的所有性能慢下来,因为你把工作分块了。
换句话说 - 考虑以下几点:
如下创建数据库表:
CREATE TABLE PlayerActions (
RowID INT PRIMARY KEY IDENTITY,
Player_Name VARCHAR(50) NOT NULL,
Monetary_Value MONEY NOT NULL
)
创建一个算法,将 25m 行分解为 100k 块。使用 LINQ / EF5 作为假设的示例。
public void ParseFullDataSet(IEnumerable<String> dataSource)
var rowCount = dataSource.Count();
var setCount = Math.Floor(rowCount / 100000) + 1;
if (rowCount % 100000 != 0)
setCount++;
for (int i = 0; i < setCount; i++)
var set = dataSource.Skip(i * 100000).Take(100000);
ParseSet(set);
public void ParseSet(IEnumerable<String> dataSource)
String playerName = String.Empty;
decimal monetaryValue = 0.0m;
// Assume here that the method reflects your RegEx generator.
String regex = RegexFactory.Generate();
for (String data in dataSource)
Match match = Regex.Match(data, regex);
if (match.Success)
playerName = match.Groups[1].Value;
// Might want to add error handling here.
monetaryValue = Convert.ToDecimal(match.Groups[2].Value);
db.PlayerActions.Add(new PlayerAction()
// ID = ..., // Set at DB layer using Auto_Increment
Player_Name = playerName,
Monetary_Value = monetaryValue
);
db.SaveChanges();
// If not using Entity Framework, use another method to insert
// a row to your database table.
运行上述一次以加载所有预先存在的数据。
在某个地方创建一个钩子,它允许您检测新行的添加。每次创建新行时,调用:
ParseSet(new List<String>() newValue );
或者如果一次创建多个,调用:
ParseSet(newValues); // Where newValues is an IEnumerable<String>
现在您可以对数据进行任何计算分析或数据挖掘,而无需担心超过 2500 万行的动态性能。
【讨论】:
注意:上面的代码是在没有编译的情况下编写的——所以我不保证它可以按原样工作,但是如果你选择按照描述的方式实现一个解决方案,它应该会引导你正确的方向。 【参考方案2】:Regex 确实需要时间来计算。但是,U 可以使用一些技巧使其紧凑。 您还可以在 C# 中使用字符串函数来避免正则表达式函数。
代码会很长,但可能会提高性能。 String 具有多种功能来剪切和提取字符并根据需要进行模式匹配。 例如:IndeOfAny、LastIndexOf、Contains....
string str= "mon";
string[] str2= new string[] "mon","tue","wed";
if(str2.IndexOfAny(str) >= 0)
//success code//
【讨论】:
以上是关于正则表达式性能下降的主要内容,如果未能解决你的问题,请参考以下文章