哪个表现更好 - 在perl中使用grep vs for循环?

时间:2018-04-04 17:22:21

标签: arrays performance perl

假设用户想要根据特定条件从数组中过滤元素。用户有两种选择。他可以使用一行grep语句或编写for循环并根据特定条件存储元素。

例如

my @array = (1, 2, 3, 4, 5, 6, 7, 8);

选项1: my @filtered = grep { $_/2 eq 0 } @array;

选项2:

foreach (@array) 
{ 
  if($_/2 eq 0) { 
    push @filtered, $_;
  } 
}

如果表现是一个因素,那么首选写作方式是什么?

3 个答案:

答案 0 :(得分:5)

第一个选项通常必须更快,因为grep具有特定的优化。

但是,该陈述过于笼统,您需要对其进行基准测试,特别是在适用于您工作的条件下。

使用Benchmark模块

的示例
use warnings;
use strict;
use feature 'say';    
use Getopt::Long;
use Benchmark qw(cmpthese);

my $time = 3;
my $size = 10_000;

GetOptions( 'time=i' => \$time, 'size=i' => \$size )  or usage();

sub w_grep {
    my @res = grep { $_/2 > $size/4 } @{$_[0]};
    return \@res;
}

sub w_for {
    my @res;
    for (@{$_[0]}) { 
        push @res, $_ if $_/2 > $size/4; 
    }   
    return \@res;
}

my @t = 1..$size;

cmpthese( -$time, {
    grep => sub { w_grep(\@t) },
    for  => sub { w_for(\@t) },
});

sub usage {
    say STDERR "Usage: $0 [--time NUM] [--size NUM]";
    exit;
}

条件在大约一半的时间内都是正确的,因此标量会被创建并经常添加。创建大量标量是昂贵的,这可能是主要因素,您的条件是多么频繁。

打印(在CentOS 7下的v5.16桌面上)

       Rate  for grep
for   948/s   -- -13%
grep 1085/s  14%   --

但是,如果我们让条件更具限制性,请说$_/2 > $size/2,那么我们就得到了

       Rate  for grep
for  1217/s   --  -6%
grep 1296/s   6%   --

当大部分时间成功时,比如$_/2 > 10,费率为

       Rate  for grep
for   945/s   -- -28%
grep 1304/s  38%   --

这表明for进一步落后,因为阵列增加了更多。

但是请记住,这取决于你在测试中做出哪些选择的许多其他方式,一些相当微妙,一些不那么微妙。

例如,首先解压缩arrayref(my @ary = @{ $_[0] };)会将差异减少到几个百分点,因为运行时主要是创建本地数组。这会引发你的测试 - 或者反映你的情况。您需要仔细选择条件。

当然,数量也随阵列大小,计算的复杂性等而变化。

答案 1 :(得分:0)

如@toolic所评论,请参阅Benchmark。我稍微改变了你的条件,以便输出数组实际填充了一些东西:

use warnings;
use strict;
use Benchmark qw/cmpthese/;

for my $arraylen (10, 100, 10_000) {
    print "#### arraylen $arraylen ###\n";
    my @array = 1..$arraylen;
    cmpthese( -1, {
        grep => sub {
            my @filtered = grep { $_%2 } @array;
        },
        loop => sub {
            my @filtered;
            foreach (@array) { 
                if ($_%2) { 
                    push @filtered, $_;
                } 
            }
        },
    });
}

输出:

#### arraylen 10 ###
          Rate loop grep
loop 1514033/s   -- -10%
grep 1683494/s  11%   --
#### arraylen 100 ###
         Rate loop grep
loop 176987/s   -- -16%
grep 210436/s  19%   --
#### arraylen 10000 ###
       Rate loop grep
loop 1794/s   -- -18%
grep 2201/s  23%   --

所以看来,至少在我的Perl机器上,grep更快。但是,与优化一样,您可能需要考虑在较大程序的上下文中这种性能影响是否显着,因为例如尝试将复杂条件填充到grep中会使代码的可读性降低。您应该对实际运行的代码进行基准测试,而不是将“grep总是快于foreach + push”作为一般规则!

答案 2 :(得分:-1)

选项2在我的机器上稍快一点......

my @array;
for ( my $i = 0; $i < 100000000; $i++ ) {
  push @array, $i;
}


my $start = time;
my @filtered = grep { $_/2 eq 0 } @array;
my $duration = time - $start;
print "Execution time: $duration s\n";

my $start = time;
foreach (@array) 
{ 
  if($_/2 eq 0) {
    push @filtered, $_;
  } 
}
my $duration = time - $start;
print "Execution time: $duration s\n";

输出:

Execution time: 296 s
Execution time: 233 s