Showing posts with label ruby. Show all posts
Showing posts with label ruby. Show all posts

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

2016-02-10

netrcの使い方。

restclientのソースコードを読んでいて便利そうだったのでメモ。

netrcにできること

netrcファイルのリード、ライト
netrcファイルにはftpのアカウントを記載する。

bash$ cat .netrc
machine 172.16.1.1
login hogehoge
password pwhogehoge

netrcはこのサイトが詳しい。

設定ファイルは以下に記述。
On Unix: $NETRC/.netrc or $HOME/.netrc (whichever is set first).
On Windows: %NETRC%\_netrc, %HOME%\_netrc, %HOMEDRIVE%%HOMEPATH%\_netrc, or %USERPROFILE%\_netrc (whichever is set first).

netrcの利用方法

1. netrcのインストール
gem install netrc

2. 読み込み
@user, @password = Netrc.read[machiname]


以上。超簡単。


pitも便利だそうです。



2015-12-19

rubyのメソッドの調査方法 Object#methodsが便利。

rubyでincludeとextendを理解するために、インプリされたメソッドの一覧を取得する場合に、どのメソッドを使っていいのか、毎回悩んでいたのでまとめてみる。


便利なのではmethodsかな。言うならば万能選手。
クラス、モジュールに対して使えばクラスメソッドを返すし、オブジェクトの場合はインスタンスメソッドを返す。falseを使うこともできる。

クラスメソッド狙い撃ちしたい場合は、singleton_method。反対にインスタンスメソッドを狙い撃ちしたい場合はinstance_methods。

repond_to?、method_defined?はあまり使わないかも。



module Test
  def test;end
  def self.test2;end #=>この書き方は意味がない
end

class Main
  extend Test
  def main; end
  def self.main2;end
end

#method_defined?
#クラスに対するインスタンスメソッドの有無を調べる
#publicなメソッド調査
Main.method_defined?(:main) # => true
Main.method_defined?(:main2) # => false
Main.method_defined?(:test) # => false
Main.method_defined?(:test2)  # => false
Main.new.respond_to?(:main) # => true
Main.new.respond_to?(:main2) # => false
Main.new.respond_to?(:test) #  => false
Main.new.respond_to?(:test2) # => false

#クラス、モジュールのインスタンスメソッドを返す
Main.instance_methods # => [:main, , , , ]
Main.instance_methods(false) # => [:main]

#レシーバがもつメソッドを返す。特異メソッドを含む
## クラス・メソッドを返す
Main.methods 
# >> [:main2, :test, :allocate, ...
## インスタンスメソッドを返す
Main.new.methods 
# >> [:main, :nil?, :===, ... 

#特異メソッドを返す.以下は同じ結果
Main.methods(false) 
Main.singleton_methods(false)
# >> [:main2]