Perlでちょっとだけ標準入力を最適化する方法

とりあえず、おさらいから。

でもって、その前にテストデータを用意します。
こんな感じでワンライナーを打つか、コピペしてください。
$ perl -E 'say $_ for 1..10' > data.txt
$ cat data.txt
1
2
3
4
5
6
7
8
9
10

今回から、use v5.10;は他の方を見習って最初に書くことにしました。

use v5.10;
use strict;
use warnings;

say <>;

こんな感じで実行する。
$ perl aaa.pl < data.txt
1
2
3
4
5
6
7
8
9
10

ちょっと無駄に長くなっちゃいましたが、
こういうことも出来ます。

use v5.10;
use strict;
use warnings;

my $sum = 0;
$sum += $_ for <>;
say $sum;

今度はこんな感じ。
$ perl bbb.pl < data.txt
55

さて、本題。
例えば、200,000行くらいあったとします。
つまり、こんな感じでテストデータを作ります。
$ perl -E 'say $_ for 1..200_000' > more_data.txt

次に、それらを配列に入れるまでの時間を計測します。

use v5.10;
use strict;
use warnings;
use Time::HiRes qw/time/;

my $start = time();
my @data = <>;
say 'count = ' . scalar(@data);
say time() - $start;

結果はこんな感じ。
$ perl ccc.pl < more_data.txt
count = 200000
0.171335935592651

あと、各要素の最後に改行が残っているので、
場合によってはchompする必要もある。(*1)

次にsplitを使う例。

use v5.10;
use strict;
use warnings;
use Time::HiRes qw/time/;

my $start = time();
my @data = split "\n", do { local $/; <> };
say 'count = ' . scalar(@data);
say time() - $start;

$ perl ddd.pl < more_data.txt
count = 200000
0.0536541938781738

この方法は、以下のページを参考にさせて頂きました。
perlで頑張って新人女子を助けた。 #paizahack_01blog.aklaswad.com

最後は、sysreadを使う例。

use v5.10;
use strict;
use warnings;
use Time::HiRes qw/time/;

my $read_size = 200_000 * 7; # どんぶり勘定

my $start = time();
my $buf;
if ( $read_size <= sysread(STDIN, $buf, $read_size) ) {
    exit( 1 ); # 全部読み込めてない
}
my @data = split "\n", $buf;
say 'count = ' . scalar(@data);
say time() - $start;

コードはアレですが、2つ目と同じくらい早いですね。
$ perl eee.pl < more_data.txt
count = 200000
0.0531890392303467

で、「こんなの、なんに使うの?」って話だけど、
例えば、標準入力で取得した改行区切りの文字列を受け取るのに、
少しでも早く配列に格納したい場合にオススメです。

おしまい。

(*1) http://perldoc.jp/func/chomp

Leave a Comment