「遅い部分は C で書き直せ」はウソだった! という話

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 でそういうことを考えるなら、 Rubyllvm-gccコンパイルして、 JIT した Ruby コードがほーにゃほにゃ、とかなのかしら。ちなみに現在の LLVM でそういうことができるかとか知らないので 100% 妄想。

あと、うちの MacOSX でやると bench2.rb の方がさらに遅いなーと思って少し調べたら、一時的に遅くなってたみたいでした。 svn up したらなおった。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h