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