2013年3月7日木曜日

リストからある条件を満たした要素を取り出す時、foreachとifまたはunlessを使った場合と、grepを使った場合とどれくらい速度が違うのか気になったので計測してみた。

何となく気になったきっかけ。

計測方法はシュワルツ変換の時に憶えたのを使う事に。

やることは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 ちゃうやろという用途」

ということで、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 コメント:

コメントを投稿