Perlでちょっとだけソートを最適化する方法
まずは、おさらいから。
use strict;
use warnings;
use v5.10;
my $str = '1,2,10,20,100,200';
my @ary = reverse( split /,/, $str );
say "------------------------------------";
printf("%5s,", $_ ) for @ary;
print "\n";
say "------------------------------------";
printf("%5s,", $_ ) for sort @ary;
print "\n";
say "------------------------------------";
printf("%5s,", $_ ) for sort { $a <=> $b } @ary;
print "\n";
say "------------------------------------";
実行すると、こんな感じ。
$ perl aaa.pl
------------------------------------
200, 100, 20, 10, 2, 1,
------------------------------------
1, 10, 100, 2, 20, 200,
------------------------------------
1, 2, 10, 20, 100, 200,
------------------------------------
という訳で、デフォルトの動作だと文字列比較によるソートが行われるので、
(値の)小さい順にソートするには、ブロック等を指定する必要がある。
で、本題。
use strict;
use warnings;
use v5.10;
use Benchmark qw/cmpthese/;
my @int_vals = reverse 1..2000000;
my @str_vals = map '' . $_, @int_vals;
cmpthese( 1, {
'int_vals' => sub { my @tmp = sort @int_vals; },
'str_vals' => sub { my @tmp = sort @str_vals; }
} );
実は、こんなのでも微妙に差が出る。
内部に詳しくないので、理由は分からないけど。
$ perl bbb.pl
(warning: too few iterations for a reliable count)
(warning: too few iterations for a reliable count)
Rate int_vals str_vals
int_vals 1.56/s -- -6%
str_vals 1.67/s 7% --
次は、値が小さい順にソートする。
$ cat ccc.pl
use strict;
use warnings;
use v5.10;
use Benchmark qw/cmpthese/;
my @int_vals = reverse 1..2000000;
my @str_vals = map '' . $_, @int_vals;
cmpthese( 1, {
'int_vals' => sub { my @tmp = sort { $a <=> $b } @int_vals; },
'str_vals' => sub { my @tmp = sort { $a <=> $b } @str_vals; }
} );
これだと、こんなに差がでる。
$ perl ccc.pl
(warning: too few iterations for a reliable count)
(warning: too few iterations for a reliable count)
Rate str_vals int_vals
str_vals 1.25/s -- -34%
int_vals 1.89/s 51% --
ただ、ベンチマークの取り方として妥当なのか分からなくて、
cmptheseの1つ目の引数を10くらいにすると差が小さくなる。
で、「こんなの、なんに使うの?」って話だけど、
例えば、標準入力で取得した改行区切りの文字列を、
値の小さい順にソートするとこんな感じになる。
use strict;
use warnings;
use v5.10;
use Benchmark qw/cmpthese/;
my @int_vals = reverse 1..2000000;
my $str = join "\n", @int_vals;
cmpthese( 1, {
'case1' => sub {
my @vals = split "\n", $str;
my @tmp = sort { $a <=> $b } @vals;
},
'case2' => sub {
my @vals = map int($_), split("\n", $str);
my @tmp = sort { $a <=> $b } @vals;
}
} );
こんな感じで差が出る。
$ perl ddd.pl
(warning: too few iterations for a reliable count)
(warning: too few iterations for a reliable count)
s/iter case1 case2
case1 1.59 -- -26%
case2 1.17 36% --
ソート前もソート後も数値として扱うなら、数値に変換しても特に問題ないし、
結果的にソートに掛かる時間も改善されるのでオススメです。
おしまい。
Leave a Comment