ruby-trunk-changes r37180 - r37195

今日はメソッド呼び出しまわりの VM の処理の最適化で大きな変更がいくつか入りました。かなり書きかわっているので trunk で手元のアプリケーション/ライブラリが動作するか確認してみて頂けるとおそらくささださんが喜ぶと思います。

ko1:r37180 2012-10-15 01:59:05 +0900

r37121 でインラインキャッシュから切り出したメソッド呼び出しのキャッシュ用の構造体 rb_call_info_t を拡張して、メソッドのID(メソッド名のシンボルの内部表現)、flag、引数の数、ブロックの命令列などメソッド呼び出しに必要な情報をまとめて持たせて、この構造体ごとやりとりするようにすることでメソッド呼び出しを高速化しています。vm_call_method() の引数が th, cfp, ci(callinfo) だけになってすっきりしました。また InstructionSequence の send/invokesuper/invokeblock 命令のオペランドが変更になっています。マイクロベンチマークでメソッド呼び出しが10%くらい高速化されているそうです。関数呼び出しの引数をマシンスタックに積むのって時間がかかるんですね……。この変更だけの影響ではないかもしれませんが。
また rb_call_info_t には call というメンバも追加されていて、メソッド呼び出しの時に呼ぶ関数の関数ポインタが格納され、処理を切り替えることができるようになっています。この時点では常に vm_call_general() という関数が使われていますが、r37186 で利用されています。
またメソッド呼び出しの flag に使うフラグに VM_CALL_ARGS_SKIP_SETUP というビットフラグが追加されていて、条件はよく調べていませんが VM_CALL_ARGS_BLOCKARG_BIT か VM_CALL_ARGS_SPLAT_BIT というフラグが立っていない時にはこのフラグが ON になって、vm_caller_setup_args() *1の呼び出しをスキップするようにしています。他のビットフラグ定数が XXX_BIT という名前なのでこれも _BIT をつけたほうがいいんじゃないかなぁ。
その他、不要そうなメモリ初期化をコメントアウトしたり、細々と高速化のための対策をしているようです。

svn:r37181 2012-10-15 01:59:12 +0900

version.h の日付更新。

nobu:r37182 2012-10-15 02:20:42 +0900

r37180 で入った(?)空行を削除しています。

ko1:r37183 2012-10-15 02:54:21 +0900

引き続きメソッド呼び出し関係の変更で、vm_call0() という関数から rb_call_info_t を作る部分とその後の本体の処理にわけて本体の処理を vm_call0_body() という関数に切り出しています。今後のリファクタリングのための布石で、そのうち vm_call0() は消すつもりとのこと。

ko1:r37184 2012-10-15 03:53:01 +0900

久々にベンチマークの追加です。attr_reader, attr_writer でできるメソッドの呼び出しのベンチマーク bm_vm1_attr_ivar.rb, bm_vm1_attr_ivar_set.rb と、flonum 用に Float の演算を繰り返すベンチマーク rb_vm1_float_simple.rb が追加されています。

ko1:r37185 2012-10-15 04:58:59 +0900

コミットログを信じるなら vm_insnhelper.c の関数の並びを変更して、 ???_call_??? みたいな関数呼び出しに関する関数がファイルの末尾に並ぶように順番を入れかえているそうです。 diff が微妙な一致で hunk を細切れにしてしまうので読めないのでここはコミットメッセージを読んだだけです。
また rb_call_info_t に opt_pc というメンバーを追加しています。これも移動されていて前後の変化がわかりにくいので、まあ変数のかわりに構造体のメンバーを使うようにしたんだろうなぁという程度の理解で。それほど重大な変更ではないと思います、多分……。
あと vm_call_bmethod() も vm_cal0() と同様に vm_call_bmethod_body() という rb_call_info_t を受け取る関数に切り出されるリファクタリングもされているようです。

ko1:r37186 2012-10-15 05:59:21 +0900

CI_SET_FASTPATH() という関数マクロを導入して、vm_call_method() で一度呼び出したらメソッドの種別に応じて rb_call_info_t::call の関数ポインタに関数をセットして、 YARV の命令(send, invokesuper など)から直接対象の関数を呼び出すことで、呼び出し情報のセットや vm_call_general() -> vm_call_method() -> 対象の関数とディスパッチする手間をスキップしています。ただし protected のメソッドの時は call をセットしないようにしています。protected だと vm_call_method() で呼び出せるかどうかのチェックをしているのでそこをスキップできないということだと思うのですが、private も同じあたりでチェックしているのになぜ protected だけ特別扱いなんでしょうか……。[追記]これは考えてみたら private の時も一度は vm_call_method() のチェックは通るわけで、private のチェックは呼ばれかた(FCALL つまり関数的な、レシーバを省略した呼びかた)だけで決まるので、一度チェックすれば同じ結果を使いまわせるのでスキップして良いのでした[/追記]
また vm_call_ivar()、vm_call_attrset() から rb_check_arity() により引数の数のチェックを削っていますが、これは VM_CALLEE_SETUP_ARG() で引数の数をチェックする処理が入っているため不要になったものだろうと思います。というのは見落していて、vm_call_method() で CI_SET_FASTPATH() で call をセットするところで呼ぶようにしていました。
この最適化は experimental なので何か問題があったら教えてね、ということなので、是非最新版で手元のアプリケーションをテストしてみて、おかしな現象があったら報告してみましょう。

ko1:r37187 2012-10-15 09:44:04 +0900

関数マクロ VM_CALLEE_SETUP_ARG() で is_lambda という引数を追加して vm_yield_setup_args() から呼ばれた時に CI_SET_FASTPATH() で rb_call_info_t::call をセットして呼び出し処理のショートカットを使わないようにしています。このパスはブロックつきメソッドに lambda(lambda や ->() で作られた Proc オブジェクト)をブロックとして渡した時にその呼び出しをメソッド呼び出しのように扱う時のところだと思うのですが、何がスキップしたらまずいのかはよくわかりませんでした。

nobu:r37188 2012-10-15 09:57:37 +0900

vm_insnhelper.c の vm_callee_setup_arg_complex() で YARV 命令の PC の型を int にしていたのを rb_num_t に修正しています。 64bit 環境でのコンパイラの警告の除去。

nobu:r37189 2012-10-15 10:44:21 +0900

新規追加されたファイルの svn property 追加。

kazu:r37190 2012-10-15 11:04:55 +0900

r37180 および r37185 の ChangeLogtypo 修正。

kou:r37191 2012-10-15 21:29:42 +0900

r37183 で追加した vm_call0_body() のコメントの typo 修正。

kou:r37192 2012-10-15 21:53:12 +0900

benchmark/ の下のベンチマークスクリプトで loop counter の i+=1 のようなインクリメントが常に空白が入らない書きかただったのを i += 1 のように前後に空白を入れるようにしています。

nobu:r37193 2012-10-15 22:14:48 +0900

r37127 で test/ruby/envutil.rb に導入したテスト用の File.xxx? のようなメソッドの結果のチェックをする assertion を assert_file という名称をやめて file_assertion というメソッドを追加して、file_assertion.exist?(path) や file_assertion.not_exist?(path) のように書けるようにしています。file_assertion は method_missing を使って、呼ばれたメソッドを File に委譲して、その結果をチェックして assert に渡すオブジェクトを返すようにしています。

nagachika:r37194 2012-10-16 00:45:07 +0900

r37185 の ChangeLog のエントリの typo を修正。

svn:r37195 2012-10-16 00:45:14 +0900

version.h の日付更新。

*1:この2つのフラグが ON の時に必要な処理を行なっている