思い出とか

x86

関数呼び出しの ABI が違うから適切な抽象化をして…とか考えたけどよくわからんくなったので、まぁ一度抽象的じゃないコード書かんとなかなかわからんよなーとざくざく書いた。おかげで ifdef だらけであまりにひどい。

最適化と一緒に考えるべきなのかもよくわからんなーという。最適化といえばこれにすごく共感。妄想だけじゃなくて実際に遊べる材料作ってみようというのが今回の趣旨。 http://b.hatena.ne.jp/hzkr/20080528#bookmark-8723758

Ruby 1.9.0-0 とか -1 とか

toregexp の引数の数が変わってて悲しかったです。仕様変えるんなら名前も変えた方がいいんと違うかなーと少し思った。 opcode から operand の長さを知る方法が無さげなので、既知の命令に関してはテーブルを前もって作っとくとかいう方針でやったので。

Mac OSX

dyld のたぶん PLT の領域で落ちてたので、すごい適当な hack で回避した。これが起きる理由ってのもたぶん興味深そうに思えたので、今度深追いしてきっちり原因を調べたいなぁと思う。

あと GC ほげほげで落ちてるとか論外。

Windows

意外と苦労なく動いて驚いた。特に Cygwin のは普通に動いてるように見える。 mingw32 は Windows のパスなので Ruby がどこにインストールされたかさっぱりわからんかったり、 /tmp 以下に保存する fopen が NULL 返して落ちたりとか、アホなことが起きていた。

次はとりあえずは放置してある命令を適当に増やしたり、 optional parameter やら throw とかやってから、最適化かなぁ。

レジスタちゃんと使うとか以前に push RAX; pop RAX; みたいなアホなコードをなんとかしていくだけでも少しは速くなりそうな気がするというか、まぁとりあえず適切なベンチマークを取るべき。

あと最適化っていうと Ruby の場合 Fixnum が変更されてない場合は times とか upto を inline 展開するとか、そういう涙ぐましい努力の方が効きそうな気がするよなーというのが。例えば benchmarkのドキュメントでは for と times と upto が比較されてるけど、絶対 while の方が速いんだよな…とやってみた。

コード

require 'benchmark'
N=10000000
Benchmark.bm do |b|
  b.report('for:   '){
    s = 0
    for i in 0...N
      s += i
    end
  }
  b.report('times: '){
    s = 0
    N.times do |i|
      s += i
    end
  }
  b.report('upto:  '){
    s = 0
    0.upto(N-1) do |i|
      s += i
    end
  }
  b.report('while: '){
    s = 0
    i = 0
    while i <= N
      s += i
      i += 1
    end
  }
end

結果。

> ruby1.9 bench_loop.rb
      user     system      total        real
for:     3.400000   0.010000   3.410000 (  3.431349)
times:   0.980000   0.000000   0.980000 (  0.976049)
upto:    0.920000   0.000000   0.920000 (  0.913937)
while:   0.480000   0.000000   0.480000 (  0.488412)

1.8 だと似たようなもんかな?と思ったらむしろ while が遅かった。

> ruby bench_loop.rb
      user     system      total        real
for:     5.730000   2.350000   8.080000 (  8.108812)
times:   5.640000   2.390000   8.030000 (  8.055811)
upto:    5.520000   2.430000   7.950000 (  7.961915)
while:   7.370000   0.010000   7.380000 (  7.409103)

ちなみに yajit

> time ./rubyjit.rb ~/test/bench_loop.rb
ruby1.9: yajit.cc:1072: VALUE Yajit::compile(): Assertion `false' failed.
zsh: abort      ./rubyjit.rb ~/test/bench_loop.rb
./rubyjit.rb ~/test/bench_loop.rb  0.01s user 0.00s system 85% cpu 0.014 total

あっという間DESU! 実に5000倍の最適化に成功!!

しゃーないので手動でやった。ちなみに for は動かない。

for:   SEGV
times: ./rubyjit.rb hoge.rb  1.14s user 1.83s system 99% cpu 2.992 total
upto:  ./rubyjit.rb hoge.rb  1.15s user 1.86s system 99% cpu 3.030 total
while: ./rubyjit.rb hoge.rb  0.18s user 0.01s system 89% cpu 0.210 total

さすがです! times と upto 遅くなってます!これはたぶん単に (system time の時間多いことから見ても) mprotect が悪いのでさっさとなんとかしよう。さすがに while は速い。

あと rb_funcall って結構 Ruby 内部にも使ってるところあるけど、よく使われてる部分がもしあるなら rb_funcall2 にした方が良くないのかにゃーとか思った。まぁそんなに負荷も無さげだけど。

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