Ruby1.9で length派 vs size派 の闘いは決着してたのであった

(8/18 8:57 一部間違いがありましたので修正しました。下の方。)
(8/18:18:00 エントリ自体がネタなんですよと明記しました。下の方)


StringやらArrayやらHashやらのインスタンス
lengthメソッドとsizeメソッドがありますが全く同じ動作をするので
個々人が好みで使い分けていました。
私は文字数が短いので、なんとなくsizeを使っていました。


この度、私はlength派に寝返りました。
と言うのは Ruby1.9系では組み込みクラス、Array、String、Hashにおいて
lengthの方がsizeより、ものすごくわずかに速いためです。
いまのところですけど。
あと、lengthメソッドをオーバーライドしてない事も条件です。


lengthとsizeの実効時間を測定してみました。
適当なArrayとStringとHashのオブジェクトを作って
lengthとsizeのメソッドを各100万回呼んで実効時間を比較しました。
1.9の方が速いのは置いておいて、lengthとsizeの差を見てください。

$ ruby -v lengthsize.rb 
ruby 1.9.2dev (2009-07-18 trunk 24186) [i686-linux]
      user     system      total        real
length	  0.220000   0.000000   0.220000 (  0.224903)
size	  0.380000   0.000000   0.380000 (  0.375388)

$ ruby1.8 -v lengthsize.rb 
ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux]
      user     system      total        real
length	  0.810000   0.130000   0.940000 (  0.963368)
size	  0.810000   0.130000   0.940000 (  0.969587)


上記の実効時間測定は以下のようなコードで確認しました。

T = 1000000

ary = [1,2,3,4,5]
str = 'abcdefghi'
has = {:hash=>10}

Benchmark.bm do |x|
  x.report "length\t" do
    T.times {
      ary.length
      str.length
      has.length
    }
  end
  x.report "size\t" do
    T.times {
      ary.size
      str.size
      has.size
    }
  end
end


何で速いかって言うと、RubyVMがlengthメソッドに最適化をしているんですが
sizeメソッドには最適化をしてないためです。
るびまYARV maniacsの第9回にlengthメソッドについては最適化していると書いてあります。
そして、sizeメソッドは最適化するともしないとも書いていなくって
実際、今のところ、最適化はされていません。


RubyVMが最適化しているかどうかはVM命令列を覗くとわかります。
" --dump=i "オプションで命令列を表示することができます。

$ ruby -v --dump=i -e '[].length'
ruby 1.9.2dev (2009-07-18 trunk 24186) [i686-linux]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
0000 trace            1                                               (   1)
0002 newarray         0
0004 opt_length       
0005 leave            
  
$ ruby -v --dump=i -e '[].size'
ruby 1.9.2dev (2009-07-18 trunk 24186) [i686-linux]
== disasm: <RubyVM::InstructionSequence:<main>@-e>======================
0000 trace            1                                               (   1)
0002 newarray         0
0004 send             :size, 0, nil, 0, <ic>
0010 leave    


opt_なんとか っていう命令列になってると最適化されてるって事です。
逆に send が使われていると、opt_なんとかに比べて遅いです。


最終的には全く同じcの関数(Arrayの場合、rb_ary_lengthという関数)が呼ばれて動作するので、機能としては全く同じになります。
ってな訳で、特に理由がある場合を覗いてsizeを使うよりlengthを使う事をオススメします。キリッ。


参考:
YARV Maniacs 【第 9 回】 特化命令
http://jp.rubyist.net/magazine/?0017-YarvManiacs


追記:
うわー、一部嘘だったので修正します。
opt_lengthはinsns.defで定義された動作をします。
Arrayの場合、全く同じcの関数を呼んでいなかったです。ごめんなさい。
でも、同じ動作をするように定義されていました。
ついでに、Stringは同じ関数を呼んでいて、Hashは呼んでいないようです。
ArrayやHashの場合は関数を呼ばずに、その場で長さを求めている分さらに少し速いハズですね。
たぶん。


追記2:
うーん、ネタをネタと書くのはなんだかアレですけど、本エントリの半分以上はネタです。。
実際の速度差は普段のアプリケーションでは気にならないと思います。
(計測してみてください)
ワンライナーとかだったらsizeと書く方が速いですよね:P
そういう最適化がなされている事についてありがたいなーと感じたりとか
中の方まで読んだりいじったりとかしていると良い事あるかもですよーっていうのが
本質だったりしますです。