同一个正则表达式在.NET中的不同运行时间执行
本文关键字:运行时间 执行 正则表达式 NET 同一个 | 更新日期: 2023-09-27 18:22:06
我正在处理一个大量使用正则表达式的项目。我使用的正则表达式非常复杂,我必须设置一个适当的超时来停止执行,这样它就不会长时间尝试匹配字符串。
问题是,我注意到在同一个字符串上运行同一个正则表达式(已编译)的执行时间不同,从17ms到59ms不等。
你知道为什么会这样吗?我使用Stopwatch
测量运行时间,如下所示:
for (int i = 0; i < 15; i++)
{
sw.Start();
regex.IsMatch(message);
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds);
sw.Reset();
}
作为参考,我使用System.Text.RegularExpressions
中.NET的默认正则表达式库。
根据评论,我以以下方式修改了代码:
List<long> results = new List<long>();
for (int i = 0; i < 150; i++)
{
sw.Start();
for (int j = 0; j < 20; j++ )
{
regex.IsMatch(message);
}
sw.Stop();
results.Add(sw.ElapsedMilliseconds);
sw.Reset();
}
Debug.WriteLine(results.Max());
Debug.WriteLine(results.Average());
Debug.WriteLine(results.Min());
其输出为:
790
469,086666666667
357
尽管如此,这种差异对我来说仍然非常显著。
既然您说您正在使用RegexOptions.Compiled
,请参阅David Gutierrez博客中的regex性能提示:
在这种情况下,我们首先执行解析操作码的工作。然后,我们还做更多的工作,使用
Reflection.Emit
将这些操作码转换为实际的IL。正如您所想象的,这种模式以增加的启动时间换取更快的运行时间:在实践中,编译启动所需的时间大约长一个数量级,但运行时性能提高了30%。然而,编译需要更多的成本。使用Reflection.Emit
发射IL会加载大量代码并使用大量内存,而这不是您永远无法恢复的内存。。。底线是,您应该只对有限的一组表达式使用此模式,您知道这些表达式将被重复使用。
这意味着第一次运行regex匹配时,将执行这项额外的工作("编译时"),并且在没有准备的情况下执行regex的所有后续时间。
然而,从.NET 2.0开始,缓存行为发生了一些变化:
在.NET Framework 2.0中,仅缓存静态方法调用中使用的正则表达式。默认情况下,缓存最后15个正则表达式,尽管可以通过设置CacheSize属性的值来调整缓存的大小。
对于任何托管平台Java/.NET来说,这都是常见的情况——例如,当他们在后台GC做一些事情时,当我们使用并发操作系统(win,linux)时,这些测试并不完全有效。您认为您在测试regex本身,但同时测试.NET、Windows和防病毒软件。
一种有效的方法是执行regex 50-1000次,总结时间和eval平均持续时间。例如重写:
sw.Start();
for (int i = 0; i < 1000; i++)
{
regex.IsMatch(message);
}
sw.Stop();
Debug.WriteLine(sw.ElapsedMilliseconds / 1000);
我认为你的成绩会很稳定。但你仍然会得到ex[15ms.18ms]的一些值范围,这在上面描述。
如果你想要真正完美的尺度(但你的问题……对不起,伙计……表明你不是真的想要它)。您需要使用PROFILER,它将在regex调用中为您提供准确的时间度量,除此之外没有任何内容。