为什么这两个正则表达式基准测试结果相差如此之大?

4
为什么这两个正则表达式基准测试差别如此之大?它们使用相同的正则表达式,一个是就地处理,另一个则通过qr//存储。 结果:
                          速率 rege1.FIND_AT_END    rege2.FIND_AT_END
rege1.FIND_AT_END     661157/s                   --                 -85%
rege2.FIND_AT_END    4384042/s                 563%                   --
                          速率 rege1.NOFIND         rege2.NOFIND
rege1.NOFIND          678702/s                   --                 -87%
rege2.NOFIND         5117707/s                 654%                   --
                          速率 rege1.FIND_AT_START  rege2.FIND_AT_START
rege1.FIND_AT_START   657765/s                   --                 -85%
rege2.FIND_AT_START  4268032/s                 549%                   --

# Benchmark
use Benchmark qw(:all);

my $count = 10000000;
my $re = qr/abc/o;
my %tests = (
    "NOFIND        " => "cvxcvidgds.sdfpkisd[s"
   ,"FIND_AT_END   " => "cvxcvidgds.sdfpabcd[s"
   ,"FIND_AT_START " => "abccvidgds.sdfpkisd[s"
);

foreach my $type (keys %tests) {
    my $str = $tests{$type};
    cmpthese($count, {
        "rege1.$type" => sub { my $idx = ($str =~ $re); },
        "rege2.$type" => sub { my $idx = ($str =~ /abc/o); }
    });
}


我对数量级差异感到惊讶。我在我的机器上运行了这个程序,并发现rege2在三个测试中分别快了37%,42%和33%。仍然更快,但不如之前那么显著。操作系统为Windows XP,使用Active State 5.8.8版本,CPU为Intel Core2 2.4GHz,内存为4GB。 - Bill Ruppert
预编译的正则表达式怎么可能比就地编译的更慢呢? - TLP
参见:http://www.perlmonks.org/?node_id=846761 - martin clayton
这可能与子程序在编译时定义有关吗? - TLP
顺便说一句:如果您不插入变量,则/o是多余的。原地正则表达式也只会被编译一次。我猜预编译版本会被字符串化和重新编译,这似乎很奇怪。您可以尝试使用 $str =~ /$re/o(在此情况下/o很重要)吗? - tsee
2个回答

3
你正在处理本质上非常快的操作,所以你需要运行更多的测试来缩小速度消耗的范围。我还将基准模型从外部(让cmpthese完成)切换为内部(for循环),这可以最小化子例程调用的开销和cmpthese必须执行的任何工作。最后,测试差异是否随着数量级的增加而扩大是很重要的(在这种情况下并不是这样)。
use Benchmark 'cmpthese';

my $re = qr/abc/o;
my %tests = (
   'fail ' => 'cvxcvidgds.sdfpkisd[s',
   'end  ' => 'cvxcvidgds.sdfpabcd[s',
   'start' => 'abccvidgds.sdfpkisd[s',
);

for my $mag (map 10**$_, 1 .. 5) {
    say "\n$mag:";
    for my $type (keys %tests) {
        my $str = $tests{$type};
        cmpthese -1, {
            '$re    '.$type => sub {my $i; $i = ($str =~ $re   ) for 0 .. $mag},
            '/abc/o '.$type => sub {my $i; $i = ($str =~ /abc/o) for 0 .. $mag},
            '/$re/  '.$type => sub {my $i; $i = ($str =~ /$re/ ) for 0 .. $mag},
            '/$re/o '.$type => sub {my $i; $i = ($str =~ /$re/o) for 0 .. $mag},
        }
    }
}

10:
                 比率 $re    失败  /$re/  失败  /$re/o 失败  /abc/o 失败 
$re    失败  106390/s           --          -8%         -72%         -74%
/$re/  失败  115814/s           9%           --         -70%         -71%
/$re/o 失败  384635/s         262%         232%           --          -5%
/abc/o 失败  403944/s         280%         249%           5%           --
                 比率 $re    结束   /$re/  结束   /$re/o 结束   /abc/o 结束  
$re    结束   105527/s           --          -5%         -71%         -72%
/$re/  结束   110902/s           5%           --         -69%         -71%
/$re/o 结束   362544/s         244%         227%           --          -5%
/abc/o 结束   382242/s         262%         245%           5%           --
                 比率 $re    开始 /$re/  开始 /$re/o 开始 /abc/o 开始
$re    开始 111002/s           --          -3%         -72%         -73%
/$re/  开始 114094/s           3%           --         -71%         -73%
/$re/o 开始 390693/s         252%         242%           --          -6%
/abc/o 开始 417123/s         276%         266%           7%           --
100: 比率 /$re/ 失败 $re 失败 /$re/o 失败 /abc/o 失败 /$re/ 失败 12329/s -- -4% -77% -79% $re 失败 12789/s 4% -- -76% -78% /$re/o 失败 53194/s 331% 316% -- -9% /abc/o 失败 58377/s 373% 356% 10% -- 比率 $re 结束 /$re/ 结束 /$re/o 结束 /abc/o 结束 $re 结束 12440/s -- -1% -75% -77% /$re/ 结束 12623/s 1% -- -75% -77% /$re/o 结束 50127/s 303% 297% -- -7% /abc/o 结束 53941/s 334% 327% 8% -- 比率 $re 开始 /$re/ 开始 /$re/o 开始 /abc/o 开始 $re 开始 12810/s -- -3% -76% -78% /$re/ 开始 13190/s 3% -- -75% -77% /$re/o 开始 52512/s 310% 298% -- -8% /abc/o 开始 57045/s 345% 332% 9% --
1000: 比率 $re 失败 /$re/ 失败 /$re/o 失败 /abc/o 失败 $re 失败 1248/s -- -8% -76% -80% /$re/ 失败 1354/s 9% -- -74% -79% /$re/o 失败 5284/s 323% 290% -- -16% /abc/o 失败 6311/s 406% 366% 19% -- 比率 $re 结束 /$re/ 结束 /$re/o 结束 /abc/o 结束 $re 结束 1316/s -- -1% -74% -77% /$re/ 结束 1330/s 1% -- -74%
您可以很容易地发现,这些测试可以分为两类,一种是源代码中带有/.../o的,另一种是没有的。由于这是一个语法上的区别,因此它给出了线索,说明这可能是编译器正在优化的情况(或者运行时以某种方式进行缓存)。(在变量被检查一次后删除检查、简化堆栈,不查看源代码很难说)。
结果也可能取决于所使用的perl版本。以上测试是在v5.10.1上运行的。

1

首先,/o 不起任何作用,因为您没有将其插入到该模式中。

现在进入问题。

1/661157 s - 1/4384042 s = 0.000,001,3 s
1/678702 s - 1/5117707 s = 0.000,001,3 s
1/657765 s - 1/4268032 s = 0.000,001,3 s

=~ $re 执行需要额外的 1.3 微秒(在我的机器上为 0.68 微秒)。=~ $re 的情况下有三个额外的 Perl 操作,这解释了一部分时间。但我不确定为什么会有三个操作。其中一个是获取 $re,但我不知道另外两个操作的作用。

>perl -MO=Concise,-exec -e"$x =~ /abc/"
1  <0> enter
2  <;> nextstate(main 1 -e:1) v:{
3  <#> gvsv[*x] s
4  </> match(/"abc"/) vKS/RTIME
5  <@> leave[1 ref] vKP/REFC
-e syntax OK

>perl -MO=Concise,-exec -e"$x =~ $re"
1  <0> enter
2  <;> nextstate(main 1 -e:1) v:{
3  <#> gvsv[*x] s
4  <1> regcreset sK/1
5  <#> gvsv[*re] s
6  <|> regcomp(other->7) sK/1
7  </> match() vKS/RTIME
8  <@> leave[1 ref] vKP/REFC
-e syntax OK

1.3微秒似乎有点过多,但实际上并不是一个显著的数量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接