すがブロ

sugamasaoのhatenablogだよ

これってどっちが早いの(3)

懲りずに

もう一つパターンを追加してみた。
そもそも何をしたいのかと言うと、「AというファイルからXXXという文字列を含む行を抽出して、行数をカウントしたい」ということ。
Ruby で実装したいので、シェルスクリプトとかでやった方が……と言うのはなしの方向で。
ちなみに、今回追加したのは、system を使ったパターンで、中間ファイルを作らない版。
内部実装的に同等なのか知らないけど、 ` で括ると戻り値を Ruby 側で受け取れるようなので、それで試してみた。
こんな感じで書くと取得できる模様。

list = `ls -l`

比べるためのデータ

2000/12/16,Hirai Ken,8th,why
2000/5/10,Hirai Ken,9th,LOVE OR LUST
2000/1/19,Hirai Ken,10th,even if
2000/12/16,Hirai Ken,8th,why
2000/5/10,Hirai Ken,9th,LOVE OR LUST
2000/1/19,Hirai Ken,10th,even if

こんな感じのデータが 10000行。1000行のデータを繰り返し連結した(うろ覚えだけど)ので、1000行目の境目で切れた分のデータがあります(123123..12で終わって、また123123..と続く感じ)。

比べるプログラム

system.rb
FILE_NAME='Book1.csv'
TEMP_FILE='system.tmp'

system("grep 'even if'  #{FILE_NAME} > #{TEMP_FILE}")

File.open(TEMP_FILE, 'r') do |file|
  puts file.readlines.size
end
system2.rb(今回追加したヤツ)
FILE_NAME='Book1.csv'

list = `grep 'even if'  #{FILE_NAME}`
puts list.split(/\n/).size
ruby_only.rb
FILE_NAME='Book1.csv'

list=[]

File.open(FILE_NAME, 'r') do |file|
  file.each do |line|
    list << line if line =~ /even if/
  end
end

puts list.size
ruby_only2.rb
FILE_NAME='Book1.csv'

list=[]

File.open(FILE_NAME, 'r') do |file|
  file.each do |line|
    list << line if line =~ /even if/o
  end
end

puts list.size

実行結果

それぞれ、10回ずつ繰り返した平均を出してます。

[system.rb]
real 0m0.0685s user 0m0.0423s sys 0m0.0247s
[system2.rb]
real 0m0.0698s user 0m0.0532s sys 0m0.0164s
[ruby_only.rb]
real 0m0.4659s user 0m0.3482s sys 0m0.1173s
[ruby_only2.rb]
real 0m0.4555s user 0m0.3375s sys 0m0.1177s

sysytem.rb と system2.rb では中間ファイルあり/なしの違いがあるので、その分差が出るかと思いきや、全然変わりませんね。誤差の範囲のような気もするけど若干遅くなっているようにも見える。
行数を取得するのに、 \n で split する以外の方法でやると変わるかもしれないですね。
ruby_only.rb と ruby_only2.rb の方は正規表現のオブジェクトを最適化している/していないの差がありますが、実際のところ通常の文字列の場合は同じオブジェクトが作られるので、内部的な差ははず。こちらは、誤差の範囲のような気もするけれど ruby_only2.rb の方が早い。

結論

やっぱりウチの環境(Fedora7)では Ruby で自力でやるより system とかのコマンドから実行した方が早いですね。
systemメソッドの代替案の `hogehoge` はもう少し改善の余地があるかもしれません。