编译的正则表达式性能不如预期?

时间:2014-03-24 07:00:31

标签: c# .net regex

我正在查看Joe Albahari在A Nutshell中的C#5.0以及关于正则表达式的第26章,他说:

  

在一些正在进行的示例中,我们使用相同的模式重复调用静态RegEx方法。在这些情况下,另一种方法是使用模式实例化Regex对象,然后调用实例方法。 。 。

// Code example from the book
Regex r = new Regex (@"sausages?");
Console.WriteLine (r.Match ("sausage"));   // sausage
Console.WriteLine (r.Match ("sausages"));  // sausages
  

这不仅仅是一种语法上的便利:在幕后。 。 。这导致(最多10次)快速匹配,代价是初始编译成本较低(几十微秒)。

好奇的我写了一个基准。该程序拆分一个字符串,迭代约3200万次静态调用和Regex实例调用的调用,以及执行相同任务的另一种方法。

class Program {
  static void Main(string[] args) {
    var str = "01/02/03/04/05/06/07/08/09/10";
    var regex = new Regex("/");
    var results = new List<Tuple<string, long>>();

    for (int j = 0; j < 128; j++) {
      var s = Stopwatch.StartNew();
      for (var i = 0; i < 1024 * 1024; i++) {
        RegexSplit(str);
      }
      s.Stop();
      results.Add(new Tuple<string, long>("Regex", s.ElapsedTicks));

      s = Stopwatch.StartNew();
      for (var i = 0; i < 1024 * 1024; i++) {
        CompiledRegexSplit(str, regex);
      }
      s.Stop();
      results.Add(new Tuple<string, long>("Compiled", s.ElapsedTicks));

      s = Stopwatch.StartNew();
      for (var i = 0; i < 1024 * 1024; i++) {
        StringSplit(str);
      }
      s.Stop();
      results.Add(new Tuple<string, long>("String", s.ElapsedTicks));

      Console.Write(".");
    }

    var resultsGroup = from it in results
      group it by it.Item1
      into g
      select new {
        Type = g.Key,
        Avg = g.Average(git => git.Item2)
      };

    resultsGroup.ToList().ForEach(it => Console.WriteLine("{0}: {1:000000000.00}", it.Type, it.Avg));
  }

  static void StringSplit(string str) {
    var split = str.Split('/');
  }

  static void CompiledRegexSplit(string str, Regex regex) {
    var split = regex.Split(str);
  }

  static void RegexSplit(string str) {
    var split = Regex.Split(str, "/");
  }
}

并得到以下结果:

Regex:    12257601.40
Compiled: 10869996.92
String:   01328636.27

根据本书,这并不是我所期望的,我怀疑实例化一个Regex需要1200万个滴答。

此次运行是在.NET 4.5,x64发布模式下运行的。

意外结果的解释是什么?

1 个答案:

答案 0 :(得分:2)

您的代码只生成了一个Regex对象的实例。要使用实际编译的Regex对象,必须指定RegexOptions.Compiled选项。这将通知Regex对象它将以这样的方式使用,即编译自身是值得的前期成本,以便它可以更快地执行。

没有自动完成的原因是,对于有限数量的运行,编译正则表达式的过程实际上将花费比节省的时间更长的时间。 Regex对象用于保存带有元数据的正则表达式,例如Regex引擎选项等,因此可以在编译或不编译的情况下使用。

进行编译的代码是:

var regex = new Regex("/", RegexOptions.Compiled);