Gauche:VMの最適化:Subr呼び出しプロトコルの検討

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を打っとく。

More ...