何时不使用RegexOptions.Compiled

ini*_*iki 51 c# regex

我理解使用RegexOptions.Compiled的优点 - 它通过以编译形式使用正则表达式而不是在运行时解释它来改进app的执行时间.虽然不建议在启动时已经很慢的应用中使用它.

但是如果我的应用程序可以承受任何轻微的启动时间增加 -
我不应该使用RegexOptions.Compiled的其他情况是什么?

就像一个笔记我多次调用这个方法 -

private static string GetName(string objString)
{
    return Regex.Replace(objString, "[^a-zA-Z&-]+", "");
}
Run Code Online (Sandbox Code Playgroud)

因此,使用'objString'的不同值调用此方法(尽管objString的值也可能重复).

你觉得在这里使用RegexOptions.Compiled是好还是不好?任何网络链接都会非常有用.
谢谢!


编辑

我用两者测试了我的网络应用程序

  • RegexOptions.Compiled,和
  • 实例Regex化为类变量

但是在我的Web应用程序所花费的时间内找不到任何重大差异 - 我在两种情况下都注意到的事情是,第一次应用程序加载它时所花费的时间比连续页面加载时花费的时间长一倍,而且无论是我是否使用RegexOptions.Compiled.

任何评论 -
为什么我的Web应用程序需要更长的时间才能使Regex首次处理并且所花费的时间在后续加载中减少到几乎一半或更少 - 是否有任何内置缓存或其他.net功能在这里有所帮助.PS如果我使用RegexOptions.Compiled与否,这件事情是一样的.

rua*_*akh 33

对于像这样的任何特定性能问题,找出哪种方式更快的最佳方法是测试两者并查看.

一般来说,编译一个正则表达式是不可能有多大的好处,除非你使用正则表达式一个不少,还是非常大的字符串.(或两者兼而有之.)我认为,在你确定自己遇到性能问题并且认为这可能有所帮助之后尝试进行优化,而不是一个随机尝试.

有关缺点的一般性讨论RegexOptions.Compiled,请参阅Jeff Atwood撰写的这篇博客文章 ; 它已经很老了,但据我所知,自编写以来,没有任何重大的相关事实发生了变化.

  • 这些链接似乎不再是最新的..NET Regex引擎在内存中存储(默认情况下)最多17个未编译的正则表达式.如果你使用18号,第一次回收.如果将正则表达式实例存储在静态变量中,则会将其存储,直到您清除它为止.您还应该记住,Compiled会增加垃圾收集器无法删除的内存压力,具体取决于您的框架版本.但是如果你的表达式是常量并且你没有使用太多不同的表达式,那么编译后的开关适合你. (2认同)

Ste*_*ham 29

需要考虑的两件事是RegexOptions.Compiled占用CPU时间和内存.

考虑到这一点,有基本上的时候,你应该只是一个实例使用RegexOptions.Compiled:

  • 您的正则表达式只运行了几次,并且运行时的净加速并不能证明编译成本是合理的.

可以说,有太多的变量可以预测并在沙子中绘制一条线.它确实需要测试来确定最佳方法.或者,如果您不想测试,那么Compiled在您这样做之前不要使用.

现在,如果您确实选择RegexOptions.Compiled了重要的是您不会浪费它.

通常,最好的方法是将对象定义为可以反复使用的静态变量.例如...

public static Regex NameRegex = new Regex(@"[^a-zA-Z&-]+", RegexOptions.Compiled);
Run Code Online (Sandbox Code Playgroud)

这种方法的一个问题是,如果您在全局范围内声明这一点,那么如果您的应用程序并不总是使用它,或者在启动时不使用它,则可能会浪费.所以稍微不同的方法是使用延迟加载,正如我在昨天写的文章中描述的那样.

所以在这种情况下它会是这样的......

public static Lazy<Regex> NameRegex = 
    new Lazy<Regex>(() => new Regex("[^a-zA-Z&-]+", RegexOptions.Compiled));
Run Code Online (Sandbox Code Playgroud)

然后,NameRegex.Value只要您想使用此正则表达式,就可以引用它,并且只在首次访问时才实例化.


RegexOptions.Compiled在真实世界中

在我的几个网站上,我正在使用ASP.NET MVC的Regex路由.这种情况非常适合RegexOptions.Compiled.路由在Web应用程序启动时定义,然后在应用程序继续运行时重新用于所有后续请求.因此,这些正则表达式被实例化并编译一次并重复使用数百万次.


drf*_*drf 9

BCL博客文章中,编译将启动时间增加了一个数量级,但后续运行时间减少了约30%.使用这些数字时,应该考虑对您期望评估超过30次的模式进行编译.(当然,与任何性能优化一样,应该测量两种替代方案的可接受性.)

如果性能对于重复调用的简单表达式至关重要,则可能希望完全避免使用正则表达式.我尝试运行一些变体,每个变体大约500万次:

注意:从先前版本编辑以更正正则表达式.

    static string GetName1(string objString)
    {
        return Regex.Replace(objString, "[^a-zA-Z&-]+", "");
    }

    static string GetName2(string objString)
    {
        return Regex.Replace(objString, "[^a-zA-Z&-]+", "", RegexOptions.Compiled);
    }

    static string GetName3(string objString)
    {
        var sb = new StringBuilder(objString.Length);
        foreach (char c in objString)
            if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '-' || c == '&')
                sb.Append(c);
        return sb.ToString();
    }


    static string GetName4(string objString)
    {
        char[] c = objString.ToCharArray();
        int pos = 0;
        int writ = 0;
        while (pos < c.Length)
        {
            char curr = c[pos];
            if ((curr >= 'A' && curr <= 'Z') || (curr >= 'a' && curr <= 'z') || curr == '-' || curr == '&')
            {
                c[writ++] = c[pos];
            }
            pos++;
        }
        return new string(c, 0, writ);
    }


    unsafe static string GetName5(string objString)
    {
        char* buf = stackalloc char[objString.Length];
        int writ = 0;
        fixed (char* sp = objString)
        {
            char* pos = sp;
            while (*pos != '\0')
            {
                char curr = *pos;
                if ((curr >= 'A' && curr <= 'Z') ||
                    (curr >= 'a' && curr <= 'z') ||
                     curr == '-' || curr == '&')
                    buf[writ++] = curr;
                pos++;
            }
        }
        return new string(buf, 0, writ);
    }
Run Code Online (Sandbox Code Playgroud)

独立执行500万个随机ASCII字符串,每个字符串30个字符,始终给出这些数字:

   Method 1: 32.3  seconds (interpreted regex)
   Method 2: 24.4  seconds (compiled regex)
   Method 3:  1.82 seconds (StringBuilder concatenation)
   Method 4:  1.64 seconds (char[] manipulation)
   Method 5:  1.54 seconds (unsafe char* manipulation)
Run Code Online (Sandbox Code Playgroud)

也就是说,编译为这种模式的大量评估提供了大约25%的性能优势,第一次执行速度大约慢3倍.对基础字符数组进行操作的方法比编译的正则表达式快12倍.

虽然方法4或方法5可以提供优于正则表达式的一些性能益处,但是其他方法可以提供其他益处(可维护性,可读性,灵活性等).这个简单的测试确实表明,在这种情况下,编译正则表达式比解释大量的评估具有适度的性能优势.

  • 还应该测试一个包含新Regex的静态字段("",RegexOptions.Compiled); (2认同)