匹配不包含bar的子字符串的性能

时间:2011-07-14 22:10:35

标签: regex perl pcre

在Perl中,以下哪种正则表达式构造最快?

  1. /foo(?>.*?bar)/s
  2. /foo(?:(?!bar).)*+bar/s
  3. /foo(?:[^b]++|b(?!ar))*+bar/
  4. 对于foobar之间距离较大的大字符串(内容为b)。 (PCRE答案也很有趣。)

3 个答案:

答案 0 :(得分:5)

use Benchmark;

找出准确的测量值。

答案 1 :(得分:0)

由于这很容易测试,我认为你要么作为脑筋急转弯,要么作为检查自己假设的一种方式;我会咬人的。 ;)

我希望#3最快。 .*?bar实际上与(?:(?!bar).)*相同,因此#1和#2都必须在每个位置进行预测。 #3只有在看到b时才会执行一次。

答案 2 :(得分:0)

  1. ab(?>.*?cd)
  2. ab(?:(?!cd).)*+cd
  3. ab(?:[^c]++|c(?!d))*+cd
  4. 在上面问题的简单情况下,使用长测试字符串,以及Perl 5.12上的以下代码,答案是第一个正则表达式胜过其他,第二个是最慢的(如可疑的)。

    这些是每秒迭代次数的结果(缩放到最慢的一次):

    1. 23.49 x iter / sec
    2. 1.00 x iter / sec
    3. 3.35 x iter / sec

    4. 使用代码:

      use v5.12;
      use Benchmark qw(:hireswallclock :all);
      
      my $str = '';
      my $ab = 'ab' x 1024**2;
      $str .= $ab . 'cd';
      $str .= $ab . 'cde';
      $str .= $ab . 'cdef';
      $str .= $ab . 'cdefg';
      $str .= $ab . 'cdefgh';
      
      printf "Test string length: %.2f MiB\n", (length($str)/1024**2);
      
      timethese(-10, {
          '0:index'       => sub { index($str, 'cdefgh') },
      
          '1:match'       => sub { $str =~ /ab(?>.*?cdefgh)/ },
          '1:fail'        => sub { $str =~ /ab(?>.*?cdefgg)/ },
      
          '2:match'       => sub { $str =~ /ab(?:(?!cdefgh).)*+cdefgh/ },
          '2:fail'        => sub { $str =~ /ab(?:(?!cdefgg).)*+cdefgg/ },
      
          '3:match'       => sub { $str =~ /ab(?:[^c]++|c(?!defgh))*+cdefgh/ },
          '3:fail'        => sub { $str =~ /ab(?:[^c]++|c(?!defgg))*+cdefgg/ },
      });
      

      输出:

      Test string length: 10.00 MiB
      Benchmark: running 0:index, 1:fail, 1:match, 2:fail, 2:match, 3:fail, 3:match for at least 10 CPU seconds...
         0:index: 11.0578 wallclock secs (10.00 usr +  0.00 sys = 10.00 CPU) @ 246.70/s (n=2467)
          1:fail: 11.0379 wallclock secs (10.05 usr +  0.00 sys = 10.05 CPU) @ 245.05/s (n=2462)
         1:match: 11.9981 wallclock secs (10.08 usr +  0.00 sys = 10.08 CPU) @ 63.80/s (n=643)
          2:fail: 12.0531 wallclock secs (10.53 usr +  0.00 sys = 10.53 CPU) @ 255.25/s (n=2688)
         2:match: 11.0573 wallclock secs (10.13 usr +  0.00 sys = 10.13 CPU) @  2.67/s (n=27)
          3:fail: 12.8325 wallclock secs (10.78 usr +  0.00 sys = 10.78 CPU) @ 266.58/s (n=2874)
         3:match: 11.8457 wallclock secs (10.09 usr +  0.00 sys = 10.09 CPU) @  9.31/s (n=94)
      

      在这些情况下,失败很快。这似乎是由于Perl可能首先扫描所有普通的子匹配字符串,如果找不到那些字符串则会失败(与index的速度相同)。