ファイルの行数を計測するスクリプトのベンチマーク

't'を1000個ならべたものを1行として
1行のファイル、101行のファイル,201行のファイル、、、1001行のファイルを作成する

for(my $n=1;$n<= 1001;$n+= 100){
  open my $t,">","text/$n.txt";
  $a=0;
  while($a++ < $n){
   print $t 't'x1000;
   print $t "\n";
  }
  close $t;
}

ファイルができたか確認する。

$ for i in text/*; do wc -l $i; done | sort -k1
       1 text/1.txt
     101 text/101.txt
     201 text/201.txt
     301 text/301.txt
     401 text/401.txt
     501 text/501.txt
     601 text/601.txt
     701 text/701.txt
     801 text/801.txt
     901 text/901.txt
    1001 text/1001.txt


以下がベンチマークスクリプト

  • oneln: 1行づつ読み込んで変数をインクリメント
  • hensu: 1行づつ読み込んで$.変数へのアクセスで行数を取得
  • pipe: open関数でパイプを開き、wcコマンドの出力をcutコマンドで切りとって取得
  • sysop: sysreadを使って、trで改行の数を計測
use Benchmark qw(cmpthese);

my $cnt;
my $line;
my $repeat = 100;
my $func = {
  oneln=>sub{
    my $file = shift;
    sub{
      $cnt = 0;
      open my $fh,"<:utf8",$file or die $!;
      $cnt++ while(<$fh>);
      close $fh;
    }
  },
  hensu=>sub{
    my $file = shift;
    sub{
      $cnt = 0;
      open my $fh,"<",$file or die $!;
      1 for(<$fh>);
      $cnt = $.;# close 前に変数に入れとく
      close $fh;
    }
  },
  pipe=>sub{
    my $file = shift;
    sub{
      $cnt = 0;
      open my $p,"echo `wc -l $file` | cut -d ' ' -f1 |" or die $!;
      $cnt = <$p>;
      chomp $cnt;
      close $p;
    }
  },
  sysop=>sub{
    my $file = shift;
    sub{
      $cnt = 0;
      open(FILE, $file) or die $!;
      while (sysread FILE, $buffer, 4096) {
        $cnt += ($buffer =~ tr/\n//);
      }
      close FILE;
    }
  }

};

for(my $r=1;$r<=1001;$r+=100){
  printf "==%03d===================================\n",$r;
  cmpthese(
    100=>{map {$_=>$func->{$_}("text/$r.txt")} keys %$func}
  );
}

このベンチマークスクリプト、どうやら、Linuxで実行したときとOSXで実行したときで
結果が違う。
Linuxの場合は、100行以上のファイルを読み込む場合には、openでwcコマンドを呼びだすpipeが爆速。
OSXの場合は、全ての場合で1行読み込んでインクリメントするonelnが高速。
ファイルシステムやキャッシュ機構の特性が関係しているのかもしれない。



P.S.
OSXの場合、cut の引数で -d ' 'を指定した場合、空白の連続も1つ1つに分割されてしまう為、このままでは正常に数値の取得ができない。

以下のようにすると取得できる。

open my $p,"echo `wc -l $file`| cut -d ' ' -f1"