高速化チューニング その3 - Scheme VM を書く
方針
- internal define を持った手続きもプロファイルとれるようにする。
- 影響範囲がすべて見通せる小さな化コード片で計測しなおす
以下メモ。
(if 1 2 3)
compile | 153 usec |
---|---|
eval | 1usec |
pass1/sexp->iform と pass3 が26回も呼ばれるのがおかしいと思ったが macro.scm を読んでいるだけだった。
小さいコードすぎると傾向が良く見えないな。難しい。
じゃあ大きめで
(library (rnrs io simple (6)) (export display) (import) (define (display x . port) (if (null? port) (sys-display x) (sys-display x (car port))))) (library (hige) (export greeting) (import (rnrs io simple (6))) (define (greeting) (display "hige"))) (import (hige)) (greeting)
pass1 | 10409(23) |
---|---|
pass2 | 203(6) |
pass3 | 16293(23) |
pass1 で重いのは
- (pass1/lambda->iform 2220 2 1110.0)
- (pass1/body->iform 1930 2 965.0)
- (pass1/sexp->iform を map-with-tail で呼び出し
- (pass1/body->iform 1930 2 965.0)
- (pass1/library->iform 852 2 426.0)
- (pass1/import->iform 521 3 173.66666666666666)
ふと短い手続きをインライン化するのが良いのではないかと思いつく。
寄り道して短い手続きをマクロでインライン化でというのをためす。
特に良く呼び出される iform のアクセサなどはこれがぴったりはまりそうだ。これで10%速くなった。
iform 頻繁にアクセスする pass3 で顕著に速くなった。
pass1 | 7887 | |
---|---|---|
pass2 | 108 | |
pass3 | 8328 |
続く。昼ご飯行ってきます。
LABEL にジャンプ
ただの switch case ではなく GCC拡張(?)のラベルのアドレスを取得して goto ってやつにしたら10%くらい速くなった。
でも期待したよりは遅かったな。
インライン展開を期待して
static Object makeBool(bool a) { return a ? Object::True : Object::False; }
これをヘッダに置いたら速くなった。
ちなみにこれを思いついたのは qprof のおかげ。 qprof++
合成命令どうしようか?
たまたま qprof を見ていたら
CONSTANT 1 ARGUMENT ;; push
みたいな命令列が何回も出てきてもったいないと思った。
CONST-PUSH みたいな1命令を作っても良いよね。(YARVとかGaucheでもやっているみたい)。
問題は合成命令にする命令列の基準だな。
- 今回のようになんとなく目についたらやる。
- コンパイル結果をソフトウェア的に解析して良くあるパターンを合成命令にする。
後者の方が良いんだろうけどどうなんだろうか。
某氏からツッコミが
>JVMのバイトコードをサラっと勉強しておくのはどうだろう?
しますします。いつもありがとうございます。
あとでまとめます。
現時点で昨日のコードと比べて 25% 速くなっている。
続く。