インスタンス変数へのアクセス

DXRuby1.3devで追加したSpriteクラスは各種パラメータを設定するのにメソッドを一通り定義してある。Spriteを継承したクラスの中からのアクセスはself.x=のように書く。通常、自分でSpriteクラスを作ったらインスタンス変数に保持するので@x=とすることになる。
なぜSpriteクラスはインスタンス変数で扱わないのかという話だが、これはDXRubyFrameworkを作った頃に検証した結果、拡張ライブラリからのインスタンス変数アクセスが極端に遅いということがわかったからだ。Ruby1.8は速かったが、Ruby1.9が遅かった。
今はどうなんだろう。試してみる。

require 'dxruby'
require 'benchmark'

Benchmark.bmbm { |bm|
  bm.report("x              ") {
    s = Sprite.new
    1000000.times {
      s.x += 1
    }
  }
  bm.report("y              ") {
    s = Sprite.new
    1000000.times {
      s.y += 1
    }
  }
}

メソッドxは内部でインスタンス変数@xへの読み書きに修正してある。xのほうがインスタンス変数で、yのほうは従来の動きだ。

Rehearsal ---------------------------------------------------
x                 0.281000   0.000000   0.281000 (  0.000000)
y                 0.188000   0.000000   0.188000 (  0.000000)
------------------------------------------ total: 0.469000sec

                      user     system      total        real
x                 0.265000   0.000000   0.265000 (  0.000000)
y                 0.204000   0.000000   0.204000 (  0.000000)

3割ほど遅い。構造体に出し入れするだけと比較してハッシュアクセスが遅いのは当たり前で、インスタンス変数化すると内部で@x=とか書けるようになって、そのばあいself.x=よりは微妙に速くなる。
じゃあ何が問題なのかと言うと、Sprite.newしたときや、描画時、衝突判定時の、内部情報への大量アクセスが極端に重くなるということが問題なのだ。Spriteは内部で値を17個持っている。これらすべてがインスタンス変数になると、もともと構造体へのアクセスなど負荷が無いに等しいところに、単純にハッシュアクセスが増える。
どのぐらい変わるかというとよくわからないから、これはもう試してみるしかないのだが。
それとは別にそもそも使い勝手としてインスタンス変数にするのがほんとにいいかどうかというのも問題ではある。