ウソみたいな本当の話。Ruby でファミコンエミュレータを書いてみました。

気になる速度ですが、自分の環境では 20 fps ちょっと出ます。ファミコンは 60 fps なので、実速の 1/3 です。Ruby3x3 （Ruby 3 は Ruby 2 の 3 倍速い）という matz の宣言が実現すれば、実速が達成されることになりますね！

試してみたい人はこんなふうに実行してください。

$ gem install ffi $ git clone http://github.com/mame/optcarrot.git $ cd optcarrot $ bin/optcarrot examples/Lan_Master.nes

SDL2 か SFML が適切にインストールされている必要があります。Debian/Ubuntu なら apt-get install libsdl2-dev で。

想定質問 なぜこんなものを作ったの？ Ruby3x3 を煽るため。OSS の開発を進めるには、開発者という馬を走らせるための「にんじん」が必要、というのは matz がたびたび言っていることです。そこで、その「にんじん」の 1 つとなればいいなと思って作りました。optcarrot は optimization carrot（最適化ニンジン）の略です。Ruby3x3 が達成されれば、開発者はファミコンゲームで遊べるというご褒美が得られます。 それから個人的に、遅い遅いと言われる Ruby の限界に挑戦してみたかったというのもあります。ファミコンエミュレータは 256 x 240 の画面を 60 fps でピクセル単位で描画する必要があります。しかし Ruby って、配列を 256 x 240 x 60 回更新するだけで 0.2 秒とか要するわけですよ。確かに遅い。残りの 0.8 秒で CPU シミュレーションとか音波合成とかしなきゃならない。無理ゲーです。この無理ゲーにどこまで応えられるか。最初にナイーブに書いたときは 3 fps とかだったんで、Ruby3x3 のストーリーに合うように 20 fps まで高速化しました。7 倍くらいの高速化は Ruby レベルの工夫次第でどうにでもなるということですね。 ごちゃごちゃ言ってるけど 60 fps でないんでしょ？ --opt を付ければ出るかも？ $ optcarrot --opt Lan_Master.nes Optcarrot はベンチマークプログラムなので、なるべく普通で綺麗なコードを書くことにしたんですが、--opt を付けると綺麗さを犠牲にして高速化します。具体的には、まず自分自身のソースコードを読み込んで、メソッドインライン展開したり、簡単な部分計算したり、fastpath をこしらえたりして、コードクローンだらけの高速だけど最悪なコードを内部的に生成します。この処理は Ruby の得意とする文字列処理なんで、正規表現を駆使して適当にやってます。で、生成されたプログラム文字列を eval することでボトルネックの処理を置き換えます。これで自分のノートパソコンでは 60 fps を達成しました。めでたい。 まあ、こんなことは普通の Ruby プログラムではやるべきでないし、これをもって「Ruby で 60 fps 達成した！」と主張するのはちょっと何か違うような気もするので、おまけ機能です。60 fps 出たときはうれしかったけどね。前向きに言えば、MRI が今後何を最適化していくべきかを考える材料提供くらいにはなるかと。*4 --opt を含めたベンチマークはこちら。 MRI 以外速くならないですね。このモードでは巨大な case 文を内部生成するんですが、case 文をジャンプテーブル的に最適化するのって MRI だけぽいのでした。*5 どうやって高速化したの？ 5 月の東京 RubyKaigi 11 で発表予定です。こうご期待。 今すぐ知りたい人はコード読めばいいと思います。 ref: http://github.com/mame/optcarrot/

