Gauche:VMの最適化:Subr呼び出しプロトコルの検討
Shiro(2006/12/04 16:48:53 PST): ちょこまかといじって性能をみてみたので、 メモしておく。
SubrにVMへのポインタを渡してみると
0.8.8現在、Subrのプロトタイプは次の通り。
ScmObj func(ScmObj *args, int nargs, void *data)
argsはVM上のスタックに積まれた引数ベクタを直接指してる。nargsが 渡された引数の数。dataはSubr作成時に渡すopaque pointerで、 クロージャ的なものをC側で実装したい時に使う。
さて。組込みのSubrの中には、VMへのポインタを必要とするものが ちょくちょくある。今はその都度Scm_VM()を呼んでVMへのポインタを 取っている。Scm_VM()はpthread環境ではpthread_getspecificを呼ぶ。
だったら、Subrを呼ぶ時にそれを呼んでるVMのポインタを渡してやれば Scm_VM()のオーバヘッドが減らせるか、と思ってやってみた。 Subrのプロトタイプはこうなる。
ScmObj func(ScmVM *vm, ScmObj *args, int nargs, void *data)
結果だが、とりあえずSubrをinner loopで積極的に呼ぶような わざとらしいマイクロベンチマークを実行してみると若干差が出る (Pentium4, 2GHz, Linux 2.6.17, gcc4.0.2)。
(define (main args) (let loop ((i 0)) (when (> i 100000000) (exit 0)) (%gcd 7 3) ;; インライン展開されないsubrなら何でも良い (loop (+ i 1))))
user time | |
vmポインタ無 | 32.46s |
vmポインタ有 | 35.23s |
引数ひとつ余分に積んで1回あたり28ns? どうなんだろ。 マシンスタックがキャッシュに載ってるって考えるとちょっと オーバヘッドが大きすぎる気もする。
Scm_VM* 系APIとC Continuation APIも変更
Scm_VMなんとか、というAPIは原則として「VMの状態を変更し、 それから戻った後で効果が表れる」ようなAPIだ。例えばScm_VMApplyは Schemeのapplyに相当するが、Scm_VMApply内で渡された関数を呼び出す わけではなく、「次にVM loopに戻った時に渡された関数が呼び出される」 ようにVMの状態を変更する。
なので、Scm_VM*系APIはほぼ常にpthread_getspecificで現在のVMへの ポインタを得ている。この系統のAPIを、第一引数に常にvmへのポインタを 渡すように変更してやれば、(1)既にcallerがvmへのポインタを得ている 場合にpthread_getspecificの呼出しが減らせる (2)APIが統一されて 美しい、というメリットがありそう。
さらに、C Continuationもその内部でScm_VMApplyやScm_VMPushCCを 呼ぶことが多いので、vmポインタを引数で渡してやることにする。
という変更をえいやとやってベンチを取ってみた。
まず、pthread_getspecific経由での現在のVMポインタを取得した回数。 no VMPTRが従来のAPI。with VMPTRがvmポインタを極力持ち回るように したもの。
no VMPTR with VMPTR -------------------------------------------- compplot 906128 738474 rgs 888160 37471 ssax 26924550 22644637 anc2 2741097 71022 ack 1429 528 tak 1364 495
そして実行時間。
no VMPTR with VMPTR -------------------------------------------- compplot 12.962 13.335 rgs 11.224 11.327 ssax 12.768 13.130 anc2 14.663 14.759 ack 4.384 4.499 tak 30.565 30.547
結局、引数が増えるオーバヘッドの方が微妙に強く出てしまっているっぽい。
これまでの変更は実は伏線であった。ある最適化のアイディアが、 現在のVMへのポインタを取得しまくる必要があるので、もし それを持ち回るようにしても性能に影響が出ないならその変更を 先に入れてしまおうと思ったのだ。でもこれまでの結果からすると 微妙に悪くなるっぽいので、この変更は今は入れないことにする。
実験用コードが入ったソースはタグvmptr_experiment0_8_8を打っとく。