http://d.hatena.ne.jp/Gimite/20080802/1217647596
でちょっと思い出した、前からやってみたかった実験をやってみたらだいたい思った通りな感じだった。
class Fixnum def times i = 0 s = self while i < s yield i i += 1 end end end s = 0 1000000.times{|i| s+=i } p s
みたいなコードを書くわけです。ご存知の通り、 Ruby の Fixnum#times は C 言語で実装されてます。それを Ruby で実装しなおしたら当然遅くなります。ホント時間のムダです。実はそうでもないんです。
> time ruby1.9 bench.rb 499999500000 ruby1.9 bench.rb 0.10s user 0.00s system 91% cpu 0.114 total i@u4 ~/test > time ruby1.9 bench2.rb 499999500000 ruby1.9 bench2.rb 0.11s user 0.00s system 92% cpu 0.121 total
後者が class Fixnum の部分をコメントアウトしたコードの結果なんですけど、まぁ Ruby で車輪再発明しちゃったヤツの方が速かったりするんですね。不思議ですかね。
タネはこう、あまりサックリとは説明できないんですけど、 Ruby => C => Ruby って往復がそれなりにコストかかる、っていう話かと思います。 setjmp なんかをはじめとしてごちゃごちゃやってるはずなので。まぁこの例だと僅差なのでそんな気にすることでもないでしょうけど。
でもまぁ、 JIT とかしはじめると、こう組み込み関数が C で書いてあることがさらに不利になったりするかなーとか妄想するわけです。 Rubinius とか大部分を Ruby で実装とかアホかーとか思ってたけど、まぁアホは私の方だったかもなぁとかそういう。ランタイムを VM に近いレイヤーでやる、ってのは Tamarinもそうらしいし。 CRuby でそういうことを考えるなら、 Ruby を llvm-gcc でコンパイルして、 JIT した Ruby コードがほーにゃほにゃ、とかなのかしら。ちなみに現在の LLVM でそういうことができるかとか知らないので 100% 妄想。
あと、うちの MacOSX でやると bench2.rb の方がさらに遅いなーと思って少し調べたら、一時的に遅くなってたみたいでした。 svn up したらなおった。