2016-06-30

File.open&each_lineとreadlines&eachのどちらが早いか問題


はじまり

以前から気になっていたeachlineを使うか、readlinesをつかうか問題。

「趣味の問題」と片づけていたが、処理スピードに差があるとしたら、そうも言ってられない。

というわけで、「eachline」と「readlines」で同じ処理を行い、処理時間を比較してみた。

負荷の定義

まずは、処理時間の差につながるプログラムの「負荷」を定義してみたい。

負荷は、「CPU負荷」と「IO負荷」の2種類に大別されるが、今回は前者の「CPU負荷」について考えてみたい。

「CPU負荷≒CPU使用率」これも2種類(「システムCPU時間」と「ユーザCPU時間」)とにわけられる。

「システムCPU時間」はシステムコール等の実行に費やした時間の合計、
「ユーザCPU時間」はそれ以外(超ざっくりです)

よって、OSの機能を呼び出さない限り、システムCPU時間はゼロのままとなる。
また、上述のキーワードは、以下のように言い換えることができる。
 
 システムCPU時間(UserTime)  = カーネル空間(スペース)
 ユーザCPU時間(SystemTime) = ユーザ空間(スペース)


CPU時間と空間。一見違う種類の言葉に見えるが、ネットの記事をいろいろ見ていると、どうも同じコンテキストで使われているようだ。


ユーザスペースでプログラムが実行されたときの時間

次に気になるのが「空間」の定義である。これはこのへんLinux純正の説明が詳しい。

以下の図をみれば一目瞭然だろう。



測定

以下のコードで各スペースでの処理時間の測定を行なった。時間測定には「benchmarkモジュール」を利用した。


```rb
require 'benchmark'

Benchmark.bm 10 do |r|
  i = 0
  r.report "open + each" do
    File.open("test.txt").each_line do |j|
       j
    end
  end

  r.report "readlines" do
    File.readlines("test.txt").each do |j|
       j
    end
  end
end
```

eachlineとreadlinesの実装の違い

cのソースコードで実装の違いを確認してみる。

まずはreadlines。

 readlines  : io_s_readlines => rb_io_readlines => rb_io_getline_1 
 の順で関数を呼び出して、1行ずつフェッチして、arrayに追加、最後にarrayを返却。

続いてeach_line。

 each_line  : rb_io_each_line => rb_io_getline_1
 で1行ずつブロックにyieldするだけ。よって1行ずつストアして行列を拡大させる処理が必要ない。よって早いはず??


以下はスタックオーバーフローでの質問回答
Digging into the source, readlines is implemented by io_s_readlines which calls rb_io_readlines. rb_io_readlines calls rb_io_getline_1 to fetch line and rb_ary_push to push result into the returning array.
each_line is implemented by rb_io_each_line which calls rb_io_getline_1 to fetch line just like readlines and yield the line to your logic with rb_yield.
So, there is no need to store line results in a growing array for each_line, no array resizing, copying issue.


結果

概ね「readlines」が約2倍遅い結果となった。
(※まったく逆の結果readlinesが2倍早くなる場合もあったが原因は不明。)


                    user      system      total         real
open + each  0.016000   0.000000   0.016000 (  0.021503)
readlines     0.031000   0.000000   0.031000 (  0.028004)


リンク

http://stackoverflow.com/questions/15677521/in-ruby-file-readlines-each-not-faster-than-file-open-each-line-why

No comments: