假设用户想要根据特定条件从数组中过滤元素。用户有两种选择。他可以使用一行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, $_;
}
}
如果表现是一个因素,那么首选写作方式是什么?
答案 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