ゲームのRubyレベルのプロファイルは取ったからCレベルのプロファイルを取ってみるかと。
MacだとInstrumentsとかSharkというアプリでプロファイルが取れるのでやってみた。
が、結果がよくわからん。
どちらも一定間隔でサンプルをとってその間実行されてた関数のスタックトレースを取っておく。で、同じ関数が沢山ヒットすればその間数がいっぱい実行されてたんだなぁとかそんな感じっぽい。
ちょっと求めてるものと違うなぁ。これだとちょっとの時間しか実行されないけどたっくさん呼ばれる関数の実行時間が怪しくなっちゃう。あと出力の見方がさっぱりわかんねー。
じゃあgprofでいいやと-pgつけてビルドしてみたんだけど、なんかgmon.outはでないしユニバーサルバイナリなgprofにはアーキテクチャが違うので駄目ですとかいわれちゃう。
調べたらIntel Macではgprofは使えないとのこと。え、なんで?
代わりにSaturnてのがあるのでこれをつかえと。-finstruments-functionsてのをつけてコンパイルし、-lSaturnをつけてリンクすればよい。
しかしつけたらRubyのビルドが通らん。途中でminirubyで何かしてるところで落ちてしまう。なんだろう。
ruby本体のビルドはちょっと無理矢理だけどできたので、それをコピってきて実行。動いたぜ。さすがにちょっと重いな。
終わるとSaturnで読み込めるファイルができてるので読み込めば…あれ、ファイルがでかすぎて読み込めないって?え、10GBってなにこれ?
ずいぶんでかいファイルができるなーと途中で打ち切ってみたが、まだ5.5GB。うわ。やっぱり読み込めない。
うーん何か間違ってるんだろうか。なんか環境変数でオプションが付けられるのでこれ指定しないと駄目なのかな。
ドキュメント見ると超でかいバイナリとかプロファイル取ると結果も超でかくなるよと。いやバイナリはそんなにでかくないんですが。たぶんsetjmpとかやってるのに対応してないんだと思う。
という辺りであきらめた。普通にgprofが使えれば良かったんだけど、gprofでも駄目かもな。
プロファイル取りに飽きたのでRubyのVM最適化オプションをいじって遊ぶ。
とりあえずvm_opt.hで全部有効に…はまずそうなので命令やオペランド融合なんかの融合系とスタックキャッシングを有効にしてみる。
あれ、ビルド通らん。なんかスタックキャッシング用のソース自動生成のスクリプトが間違ってたので修正して通す。
ビルドは通ったけど落ちたよ!駄目か。
融合系は有効にしても通ったのでゲームのベンチマークモードを通してみると…!
うん。変わってないね。
まあ期待してなかったしね。そんなに速くなるなら最初から有効だってば。
Sharkのサンプリング結果を見るとVMの中でものすごい時間かかってたりはしなかったし、RubyのVMは十分高速かも。
いやそれをはっきりさせるためにCレベルのプロファイルをちゃんと取りたいんですけどねぇ。
あとset_trace_func用のトレース命令を使用するオプションがあったので外したら微妙に速くなった。22秒中0.5秒弱くらい。微妙。しかしリリース時には省略したら気分的にいいかもねくらい。
そういやVM命令を眺めたりして前のset_trace_funcでメソッドのリターンイベントが飛んでこない問題の原因に気づいた。
breakは例外と同じような感じに実装されてて、コンパイル時点でブロックがくっついてるメソッド呼出しの次の命令にbreakを捕捉したらここに飛べマークをつけておく。なのでbreakした場合はメソッド呼び出しの直後にgotoなわけだ。
メソッドに入った/出たというイベントを発生させる命令は、メソッドの内部でメソッド本体の最初と最後にくっつくようになっている。
でもメソッドの中からbreakされると、メソッド呼出元の直後までgotoなので途中の命令は全部すっ飛ばしですよ。
どうしても捕捉したいなら、各メソッドの最後に必ず例外とかbreakの捕捉ポイントを作っておいて、終了イベントを呼んでから再度投げ直すのかなぁ。ensureみたいな感じ。C++の例外処理みたいな感じ。
当然めっちゃ遅くなるけどね!これは非常に難しい問題だ。
トレースの命令埋め込みはあきらめて、フレームの巻戻しはしてるだろうから、そこでフックかけられるようにはできないかな。
うーん、VMのソース読んだけど難しくて簡単には理解できん。幸い検索するとソース読んだ人のメモがいくらか見つかるのでそれも参考にしよう。