[Melbourne-pm] grep vs first vs foreach
Paul Fenwick
pjf at perltraining.com.au
Fri May 11 01:48:56 PDT 2007
G'day Everyone,
At Perl Mongers the other night there was a discussion regarding the fastest
way to determine if an element exists in a list. Without building a hash,
three proposed were given:
* Use List::Util's first()
* Use Perl's in-built grep() in scalar context.
* Use a foreach loop.
Interestingly enough, after some testing I've discovered the foreach loop
wins in terms of speed. first() suffers from a high cost of entering a
subroutine for each list element, whereas grep() will search *all* items to
return the total number of matches. Our foreach loop, on the other hand,
can end immediately when the item is found.
Using a 'return' from inside the grep() block allows short-cutting which
improves its speed significantly, although foreach still wins if the item is
found early in our list. A pure grep() is actually fastest in our failure
case (where the item cannot be found).
I don't know if grep() is smart enough to return immediately upon finding an
item if called in a boolean context. If not, then this seems like an
obvious optimisation that could be implemented.
Raw data and original code below:
---cut here---
First item:
Rate grep first grep-return foreach
grep 455/s -- -99% -99% -100%
first 44823/s 9741% -- -29% -97%
grep-return 63232/s 13782% 41% -- -95%
foreach 1365750/s 299745% 2947% 2060% --
Middle item:
Rate grep first foreach grep-return
grep 419/s -- -25% -40% -46%
first 559/s 34% -- -20% -28%
foreach 700/s 67% 25% -- -9%
grep-return 772/s 84% 38% 10% --
Non-existent item:
Rate first foreach grep-return grep
first 297/s -- -18% -27% -35%
foreach 362/s 22% -- -11% -21%
grep-return 409/s 38% 13% -- -10%
grep 455/s 53% 26% 11% --
---cut here---
Cheerio,
Paul
#!/usr/bin/perl -w
use strict;
use List::Util qw(first);
use Benchmark qw(cmpthese);
my @list = (1..10_000);
compare(1,"First item");
compare(5000,"Middle item");
compare(-1,"Non-existent item");
sub compare {
my ($element,$msg) = @_;
print "\n\n$msg:\n\n";
cmpthese(0,
{
'grep' => sub {
grep {$_ eq $element } @list;
},
'grep-return' => sub {
grep {return 1 if $_ eq $element } @list;
},
'foreach' => sub {
foreach (@list) {
last if $_ eq $element;
}
},
'first' => sub {
first { $_ eq $element } @list;
},
}
);
}
__END__
--
Paul Fenwick <pjf at perltraining.com.au> | http://perltraining.com.au/
Director of Training | Ph: +61 3 9354 6001
Perl Training Australia | Fax: +61 3 9354 2681
More information about the Melbourne-pm
mailing list