Rubyの実行速度を考える
昔はよくこういうことをやっていた記憶がある。
ruby 1.9.3p125 (2012-02-16) [i386-mingw32]にて。そういえばしばらく更新してないから古いね。
require 'benchmark' Benchmark.bm {|x| x.report { 100000.times do Array.new(100) {1} end } x.report { 100000.times do i = 0 ary = [] while(i < 100) do ary << 1 i += 1 end end } } #=> user system total real #=> 1.109000 0.016000 1.125000 ( 1.125000) #=> 0.954000 0.031000 0.985000 ( 0.984375)
整数の1を100個詰め込んだ配列を10万回作る時間だ。上のコードがArray.newでまとめて生成しているのに対して、下のコードは分解して自分で書いている。Ruby的か手続き的かの違いとでも言おうか。問題は手続き的なコードのほうが約1割速いところだ。
この速度差の原因は、上のコードがCのコードの呼び出しとそこからのVM呼び出し100回が発生しているのに対し、下のコードはVMの命令のみで完結しているという、極めて実装依存の部分にある。ようするに、RubyのコードとCのコードをいったりきたりするのは遅い、ということだ。でもまあ、1割しか変わらんのだからそんなに目くじら立てるほどでもない。
ちなみにRuby1.8.7では関係が逆転して、下のコードのほうが4倍ほど遅くなる。つまりこれはVMの実行性能の高さゆえに発生する話であり、たとえばライブラリをある程度Ruby実装に置き換えると速くはなるだろうが、今後のVMの実装の変化によりどっちが速いかは変わってくることが予想されるし、そうなるとバージョンによってどこをRuby化するかを考えなければならないので手間である。現実的じゃない。
パーティクルシステムみたいな負荷の大きいライブラリをRubyで書こうと思うと、ちょっとでも速くするためにこういうことも考えないとあかんよなーと思いつつ、Ruby1.8.7で大変遅いというのは厄介な問題だと思ったり、まあ、だらだらと。