高速化チューニング その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/library->iform 852 2 426.0)
    • (pass1/import->iform 521 3 173.66666666666666)


ふと短い手続きをインライン化するのが良いのではないかと思いつく。
寄り道して短い手続きをマクロでインライン化でというのをためす。
特に良く呼び出される iform のアクセサなどはこれがぴったりはまりそうだ。これで10%速くなった。


iform 頻繁にアクセスする pass3 で顕著に速くなった。

pass1 7887
pass2 108
pass3 8328


続く。昼ご飯行ってきます。

find

コンパイラから数ヶ所呼ばれている find を速くするべく VM 内部に持ってくる。
実装してみたが速度はまったく分からない。勘が外れた。
勘に頼るのはもうやめよう。

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% 速くなっている。


続く。