2

其中哪一个更便宜?

$_ = 'abc123def';

s/\d+/$&*2/e;
say;

s/(\d+)/$1*2/e;
say;
4

4 回答 4

11

执行摘要:改用 5.010 的 /p。单场比赛或换人的表现$&大致相同,但整个程序可能会受到影响。它的减速是长期的,而不是局部的。


这是 5.010 的基准,我怀疑你正在使用它,因为你say在那里。请注意,5.010 有一个新/p标志,它提供一个${^MATCH}变量,该变量的作用类似于$&但仅用于匹配或替换运算符的一个实例。

与任何基准测试一样,我与一个控件进行比较来设置基线,这样我就知道无聊的部分占用了多少时间。此外,此基准测试有一个陷阱:您不能$&在代码中使用,否则每次替换都会受到影响。首先在没有$&sub 的情况下运行基准测试:

use 5.010;

use Benchmark qw(cmpthese);

cmpthese(1_000_000, {
   'control' => sub { my $_ = 'abc123def'; s/\d+/246/ },
   'control-e' => sub { my $_ = 'abc123def'; s/\d+/123*2/e;  },
   '/p'      => sub { my $_ = 'abc123def'; s/\d+/${^MATCH}*2/pe },
   # '$&'      => sub { my $_ = 'abc123def'; s/\d+/$&*2/e },
   '()'      => sub { my $_ = 'abc123def'; s/(\d+)/$1*2/e },
});

在我运行 Leopard 和普通 Perl 5.10 的 MacBook Air 上:

              Rate        /p        () control-e   control
/p         70621/s        --       -1%      -58%      -78%
()         71124/s        1%        --      -58%      -78%
control-e 168350/s      138%      137%        --      -48%
control   322581/s      357%      354%       92%        --

请注意该/e选项的大幅减速,我添加它只是为了咯咯笑。

现在,我将取消注释$&分支,我看到一切都变慢了,虽然/p似乎在这里:

              Rate        ()        $&        /p control-e   control
()         68353/s        --       -4%       -7%      -58%      -74%
$&         70872/s        4%        --       -3%      -56%      -73%
/p         73421/s        7%        4%        --      -54%      -72%
control-e 161290/s      136%      128%      120%        --      -39%
control   262467/s      284%      270%      257%       63%        --

这是一个奇怪的基准。如果我不包括control-esub,情况看起来会有所不同,这表明了基准测试的另一个概念:它不是绝对的,您所做的一切对最终结果都很重要。在这次运行中,$&看起来稍微快一些:

            Rate      ()      /p      $& control
()       69686/s      --     -3%     -3%    -72%
/p       72098/s      3%      --     -0%    -71%
$&       72150/s      4%      0%      --    -71%
control 251256/s    261%    248%    248%      --

所以,我control-e再次运行它,结果略有变化:

              Rate        ()        /p        $& control-e   control
()         68306/s        --       -3%       -4%      -55%      -74%
/p         70175/s        3%        --       -1%      -54%      -73%
$&         71023/s        4%        1%        --      -53%      -73%
control-e 151976/s      122%      117%      114%        --      -41%
control   258398/s      278%      268%      264%       70%        --

每个的速度差异也不令人印象深刻。低于 7% 的任何值都不是那么重要,因为这种差异是通过重复调用 sub 来累积错误(有时通过对同一代码进行基准测试来尝试)。您看到的细微差别仅来自基准测试基础架构。有了这些数字,每种技术在速度上几乎是相同的。您不能只运行一次基准测试。您必须多次运行它才能获得可重复的结果。

请注意,虽然/p看起来慢了一点,但它也更慢,因为$&欺骗了每个人。请注意控制中的减速。这是基准测试如此危险的原因之一。如果你不认真思考为什么它们是错误的,你很容易用结果误导自己(请参阅Mastering Perl中的完整文章,我在其中用了一整章的篇幅来讨论。)

不过,这个简单而幼稚的基准排除了$&. 让我们修改基准以处理额外的匹配。首先,没有$&效果的基线,我已经构建了一个$&必须在额外的匹配运算符中复制大约 1,000 个字符的情况:

use 5.010;

use Benchmark qw(cmpthese);

$main::long = ( 'a' x 1_000 ) . '123' . ( 'b' x 1_000 );

cmpthese(1_000_000, {
   'control' => sub { my $_ = 'abc123def'; s/\d+/246/; $main::long =~ m/^a+123/; },
   'control-e' => sub { my $_ = 'abc123def'; s/\d+/123*2/e; $main::long =~ m/^a+123/; },
   '/p'      => sub { my $_ = 'abc123def'; s/\d+/${^MATCH}*2/pe; $main::long =~ m/^a+123/; },
   #'$&'      => sub { my $_ = 'abc123def'; s/\d+/$&*2/e; $main::long =~ m/^a+123/;},
   '()'      => sub { my $_ = 'abc123def'; s/(\d+)/$1*2/e; $main::long =~ m/^a+123/; },
});

一切都比以前慢了很多,但是当你做更多的工作时就会发生这种情况,而且这两种技术再次相互干扰:

              Rate        ()        /p control-e   control
()         52826/s        --       -4%      -49%      -63%
/p         54885/s        4%        --      -47%      -61%
control-e 103734/s       96%       89%        --      -27%
control   141243/s      167%      157%       36%        --

现在,我取消注释$&子:

              Rate        ()        $&        /p control-e   control
()         50607/s        --       -1%       -3%      -43%      -59%
$&         50968/s        1%        --       -2%      -43%      -58%
/p         52274/s        3%        3%        --      -41%      -57%
control-e  89206/s       76%       75%       71%        --      -27%
control   122100/s      141%      140%      134%       37%        --

这个结果非常有趣。现在/p,仍然受到作弊的惩罚,$&速度稍快(尽管仍在噪音范围内),尽管每个人都受到了很大的影响。

同样,要非常小心这些结果。这并不意味着对于每个脚本,$&都会产生相同的效果。根据匹配的数量、特定的正则表达式等,您可能看起来不那么减速,或者减速更多。这个或任何基准测试显示的是一个想法,而不是一个决定。您仍然必须弄清楚这个想法如何影响您的特定情况。

于 2009-11-03T14:53:07.430 回答
10

来自perldoc perlvar

  • $匹配
  • $&

由最后一次成功的模式匹配匹配的字符串(不包括隐藏在 a 中BLOCKeval()由 current 包围的任何匹配BLOCK)。(助记符:在某些编辑器中类似于 &。)这个变量是只读的,并且动态地作用于当前的BLOCK.

在程序中的任何地方使用此变量都会对所有正则表达式匹配造成相当大的性能损失。请参阅“错误”

请参阅"@-"更换。

即使这些信息在文档中并不方便,您仍然可以自己计时并找出答案。

于 2009-11-03T07:19:41.890 回答
1

这是了解使用$&. 首先,您需要创建两个基准测试脚本。他们将拥有大部分共同的代码:

#!/usr/bin/perl

use strict;
use warnings;
use autodie;
use File::Spec::Functions qw( devnull );

open my $output, '>', devnull;

my $str = <<EO_LIPSUM;
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis nostrud exercitation
ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
EO_LIPSUM

use Benchmark qw( timethese );

对于第一个基准添加

### benchmark with $MATCH

timethese -1, {
    match_var => sub {
        $str =~ /commodo/;
        print $output $&;
        $str =~ /^Lorem|ipsum/ and print $output 'yes';
    }
}

对于第二个基准文件,使用

timethese -1, {
    capture => sub {
        $str =~ /(commodo)/;
        print $output $1;
        $str =~ /^Lorem|ipsum/ and print $output 'yes';
    }
}

现在,让我们运行这些基准测试(它们必须在单独的文件中):

基准测试:运行捕获至少 1 CPU 秒...
   捕获:1 挂钟秒(1.05 usr + 0.00 sys = 1.05 CPU)@ 301485.20/s
(n=315655)
基准测试:运行 match_var 至少 1 CPU 秒...
 match_var:1 挂钟秒(1.22 usr + 0.02 sys = 1.23 CPU)@ 255591.09/s
(n=315655)

也就是说,$&在这种情况下,使用会导致大约 15% 的减速。减速是由于对$&简单正则表达式匹配的影响。没有

$str =~ /^Lorem|ipsum/ and print $output 'yes';

行,$&实际上执行得更快的版本。

于 2009-11-03T14:42:48.520 回答
0
use Benchmark;

和测试。

一般来说——真的,真的,没关系。除非您正在执行数十亿次此类操作。

于 2009-11-03T08:19:41.317 回答