【问题标题】:Unexpected speed behaviour when benchmarking Perl regexs对 Perl 正则表达式进行基准测试时出现意外的速度行为
【发布时间】:2011-04-22 18:54:51
【问题描述】:

在讨论在 Perl 中使用 index() 搜索子字符串的相对优点时,我决定编写一个微型基准测试来证明我之前看到的在查找子字符串时索引比正则表达式更快。这是基准测试代码:

use strict;
use warnings;
use Benchmark qw(:all);

my @random_data;
for (1..100000) {
  push(@random_data, int(rand(1000)));
}

my $warn_about_counts = 0;
my $count = 100;
my $search = '99';

cmpthese($count, {
  'Using regex' => sub {
    my $instances = 0;
    my $regex = qr/$search/;
    foreach my $i (@random_data) {
      $instances++ if $i =~ $regex;
    }
    warn $instances if $warn_about_counts;
    return;
  },
  'Uncompiled regex with scalar' => sub {
    my $instances = 0;
    foreach my $i (@random_data) {
      $instances++ if $i =~ /$search/;
    }
    warn $instances if $warn_about_counts;
    return;
  },
  'Uncompiled regex with literal' => sub {
    my $instances = 0;
    foreach my $i (@random_data) {
      $instances++ if $i =~ /99/;
    }
    warn $instances if $warn_about_counts;
    return;
  },
  'Using index' => sub {
    my $instances = 0;
    foreach my $i (@random_data) {
      $instances++ if index($i, $search) > -1;
    }
    warn $instances if $warn_about_counts;
    return;
  },
});

令我惊讶的是它们的表现如何(在最近的 MacBook Pro 上使用 Perl 5.10.0)。按速度降序排列:

  1. 带有文字的未编译正则表达式 (69.0 ops/sec)
  2. 使用索引(61.0 ops/sec)
  3. 标量的未编译正则表达式 (56.8 ops/sec)
  4. 使用正则表达式(17.0 ops/sec)

谁能解释一下 voodoo Perl 使用什么来获得两个未编译的正则表达式的执行速度以及索引操作?这是我用来生成基准的数据中的问题(在 100,000 个随机整数中寻找 99 的出现)还是 Perl 能够进行运行时优化?

【问题讨论】:

  • 所以从每个人的 cmets 来看,很明显我没有意识到我错过了 /o 标志来实际编译代码。我已经添加了这个,我发布的时间并没有显着偏离,除了“未编译的带有标量的正则表达式”现在与索引相当。
  • 这一切似乎都归结为使用来自标量的正则表达式,即qr/99/ 导致执行速度比任何内联正则表达式都慢,这与未优化的子字符串搜索相当。跨度>

标签: regex perl benchmarking


【解决方案1】:

批发改版

鉴于@Ven'Tatsu 的评论,我稍微更改了基准:

use strict; use warnings;
use Benchmark qw(cmpthese);

use Data::Random qw( rand_words );
use Data::Random::WordList;

my $wl = Data::Random::WordList->new;

my @data_1 = (rand_words( size => 10000 )) x 10;
my @data_2 = @data_1;

my $pat = 'a(?=b)';
my $re = qr/^$pat/;

cmpthese(1, {
    'qr/$search/' => sub {
        my $instances = grep /$re/, @data_1;
        return;
    },
    'm/$search/' => sub {
        my $search = 'a(?=b)';
        my $instances = grep /^$search/, @data_2;
        return;
    },
});

在带有 ActiveState perl 5.10.1 的 Windows XP 上:

 评分 qr/$search/ m/$search/
qr/$search/ 5.40/s -- -73%
m/$search/ 20.1/s 272% --

在带有草莓perl 5.12.1 的 Windows XP 上:

 评分 qr/$search/ m/$search/
qr/$search/ 6.42/s -- -66%
m/$search/ 18.6/s 190% --

在 ArchLinux 上使用 bleadperl:

 评分 qr/$search/ m/$search/
qr/$search/ 9.25/s -- -38%
m/$search/ 14.8/s 60% --

【讨论】:

  • Regexp 编译在 5.10.x 中得到了一些关注,在 5.8.8 下,qr/$search/m/$search/ 相差在 1% 以内。另一个问题是模式的复杂性。将模式更改为 9{2} 会推动 qr/$search/ 对我来说稍微领先一点。任何更复杂的表达式(如9(?=9).),它都会成为m/$search/ 的明显赢家。
  • 我很好奇 /o 是否会有所作为,但现在还不足以真正尝试。 :)
  • 感谢您指出Data::Random::WordList 模块@Sinan,这会给我带来更真实的结果。至于时间,重点是建立一个基准,将使用索引检测子字符串与使用正则表达式进行比较。因此,更改正则表达式实际上是在改变目标职位
  • @andeyatz 检查我之前帖子的版本,了解qrindex 的时间安排。对我来说,奇怪的是index 与模式匹配之间没有任何区别。第一个基准测试的反直觉结果是 qr/$var/m/$var/ 慢得多,尽管编译模式和 re debug 的实际输出似乎与我相同。
  • 很抱歉之前没有注意到这一点。这与我当时看到的相同。当我打开re debug 时,它似乎说所有的正则表达式都是相等的,但微基准不会说谎。似乎在使用 qr// 正则表达式而不是内联正则表达式时会受到很大的惩罚。这可能是由使用 Perl 的线程编译版本引起的吗?
【解决方案2】:

好吧,您的案例“使用正则表达式”太慢了,因为您每次都在编译它。尝试将其移出子程序。

【讨论】:

  • 但是 OP 不是专门在比较编译速度吗?因此,将其拉出循环在这里没有帮助,它会使基准测试变得毫无意义。 :)
  • @Ether 但是以这种方式使用已编译的正则表达式是没有意义的。编译的正则表达式只有两个好处:它们被编译一次,并且它们被正确地包装在非捕获组中,即使插入到其他正则表达式中(例如my $any_foo = qr/foo/i; my $any_foo_lower_bar = qr/${any_foo}bar;)也会保持你给它们的选项
  • 将正则表达式移到子例程之外对基准测试没有影响。
【解决方案3】:

Perl 优化了很多东西。没有特殊正则表达式功能和文字字符的模式允许 perl 的正则表达式引擎简化许多事情。使用use re 'debug' 可以向您展示幕后实际发生的事情。

【讨论】:

  • 我希望与预编译的qr/$var/ 匹配以表示m/$var/ 的速度上限,但也许我的期望是错误的。
  • 所有正则表达式根据re pragma 以相同的方式编译
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
  • 2012-07-16
  • 2013-01-14
相关资源
最近更新 更多