リストからある条件を満たした要素を取り出す時、foreachとifまたはunlessを使った場合と、grepを使った場合とどれくらい速度が違うのか気になったので計測してみた。
何となく気になったきっかけ。
@mkamimura 昔は grep 速いって言われてましたが、今はどうなんでしょうね
— ζさん (@zetamatta) 2013年3月7日
計測方法はシュワルツ変換の時に憶えたのを使う事に。
やることは1から10^6(百万)までの整数から偶数を取り出すという単純な事。
コード(BBEdit)
sample.pl
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use 5.016;
use Benchmark qw(:all);
my $n = 10 ** 6;
my @nums = (1..$n);
cmpthese( -5, {
"foreach, unless" => q{
my @result = ();
for (@nums) {
push @result, $_ unless $_ % 2;
}
},
"grep" => q{
my @result = grep {$_ % 2 == 0} @nums;
},
});
timethese( -5, {
"foreach, unless" => q{
my @result = ();
for (@nums) {
push @result, $_ unless $_ % 2;
}
},
"grep" => q{
my @result = grep {$_ % 2 == 0} @nums;
},
});
入出力結果(Terminal)
$ ./sample.pl
Rate foreach, unless grep
foreach, unless 1115645/s -- -51%
grep 2266773/s 103% --
Benchmark: running foreach, unless, grep for at least 5 CPU seconds...
foreach, unless: 3 wallclock secs ( 5.01 usr + 0.03 sys = 5.04 CPU) @ 1081056.35/s (n=5448524)
grep: 3 wallclock secs ( 5.00 usr + 0.04 sys = 5.04 CPU) @ 2255106.94/s (n=11365739)
$
上記の場合はgrepを使った方が約2倍速いみたい。(ってことでいいのかな。。)
追記:
grep の方が速いってことになりますが、これは grep を本来の用途として使ったケースですね。昔は抽出しないような「grep ちゃうやろという用途」でも grep が速いとか言われてましたね / “Perl - foreac…” htn.to/DiFbXiA8vi
— ζさん (@zetamatta) 2013年3月7日
これは grep を本来の用途として使ったケースですね。昔は抽出しないような「grep ちゃうやろという用途」
ということで、foreachのif(あるいはunlessとかの条件、評価)も無しで、ただリストの内容を新しいリストに移すだけのも計測してみる事に。(というか、この場合は「foreachちゃうやろという用途」にもなっちゃうのかな〜「grep ちゃうやろという用途」っていう用途って何だろう。よくある事、単純な事だったりするのかな。。全然、全く、思いつかない>_<けど、これでforeachとgrepの違いは分かるのでとりあえず良しとする事に。あとついでにpushとインデックスによる代入の場合の違いも分かるように追加。あと一番単純な方法の配列のスライスとか、mapとかも追加。)
コード(BBEdit)
sample.pl
#!/usr/bin/env perl
use strict;
use warnings;
use utf8;
use 5.016;
use Benchmark qw(:all);
my $n = 10 ** 6;
my @nums = (1..$n);
cmpthese( -5, {
"slice" => q{
my @result = @nums[0..$#nums];
},
"foreach, push" => q{
my @result = ();
for (@nums) {
push @result, $_;
}
},
"foreach, index" => q{
my @result = ();
my $i = 0;
for (@nums) {
$result[$i] = $_;
$i++;
}
},
"grep" => q{
my @result = grep {1} @nums;
},
"map" => q{
my @result = map {$_} @nums;
},
});
timethese( -5, {
"slice" => q{
my @result = @nums[0..$#nums];
},
"foreach, push" => q{
my @result = ();
for (@nums) {
push @result, $_;
}
},
"foreach, index" => q{
my @result = ();
my $i = 0;
for (@nums) {
$result[$i] = $_;
$i++;
}
},
"grep" => q{
my @result = grep {1} @nums;
},
"map" => q{
my @result = map {$_} @nums;
},
});
入出力結果(Terminal)
$ ./sample.pl
Rate foreach, index slice foreach, push grep map
foreach, index 1081248/s -- -3% -18% -54% -57%
slice 1120062/s 4% -- -15% -53% -56%
foreach, push 1317695/s 22% 18% -- -45% -48%
grep 2376337/s 120% 112% 80% -- -6%
map 2523952/s 133% 125% 92% 6% --
Benchmark: running foreach, index, foreach, push, grep, map, slice for at least 5 CPU seconds...
foreach, index: 5 wallclock secs ( 5.04 usr + 0.01 sys = 5.05 CPU) @ 1106963.37/s (n=5590165)
foreach, push: 5 wallclock secs ( 5.02 usr + 0.00 sys = 5.02 CPU) @ 1354918.33/s (n=6801690)
grep: 6 wallclock secs ( 5.04 usr + 0.02 sys = 5.06 CPU) @ 2490838.93/s (n=12603645)
map: 6 wallclock secs ( 5.21 usr + 0.02 sys = 5.23 CPU) @ 2549747.80/s (n=13335181)
slice: 5 wallclock secs ( 5.23 usr + 0.02 sys = 5.25 CPU) @ 1130087.24/s (n=5932958)
$
配列のスライスが一番単純で簡単だし、速いのかな〜と思ったらそうじゃなかった事に驚いた!(Rateが大きい程速いって事でいいのかな。勘違いしてたりして><w)
0 コメント:
コメントを投稿