mini-python 高速化

現在のmini python VM は、VMのプロトタイプTiviにちなんで、自分の中ではTivi2と呼んでおります。

そんなことはどうでもよい。

VMの速度が思ったほど出ないなー、と思ってプロファイリングをとってみたら、STLvectorをふんだんに利用しているのが、どうも遅さの一員らしい、ということがわかってきた。そこで、限定的な場面で(といってもかなり沢山呼ばれる)配列を使うように切り替えてみたところ、劇的に高速化することができた。

サンプルプログラムにフィボナッチ数の計算、数値積分などをするsamplesというコード群があるのだけれど、そいつらをまとめて10回繰り返し実行する run_simples.sh というシェルスクリプトを実行して、その実行時間を測ってみたところ、次のような結果になった。計測はtimeコマンドで、5回走らせて平均をとった。画面出力は、どちらも /dev/null にリダイレクトしてある。

本家python(sec) Tivi2 (sec)
12.84 13.67

ε( v°ω°)ε( v°ω°)ε( v°ω°)ε( v°ω°)ε( v°ω°)


しかし、辞書式のアクセスが今のところ線形探索になっているので、sor.py という熱伝導方程式を解くプログラムに3分以上かかってしまうんだよなぁ。本家pythonなら5秒程度で終了するのだけれど。今ハッシュを使って実装しなおしているけど、はたしてそこまで辿りつけるか。

ハッシュ以外に思いつく高速化としては、forループのコンパイルだろうか。sor.py だけでなく、サンプルにあるforループはどれも for var in range(...) という形になっている。この形になっていると最適化をかけれそうなので、それで幾分か早くなるんじゃないかと思っている。

今のforの実装だと、カレントフレームのスタック上にシーケンスのインデックスを保持して、それを1回ずつインクリメントして、シーケンスの長さと比較して、シーケンスから値を取り出して、ループの中身を実行して、ということになっている。とりあえず書いた、という状態なので相当おばかな感じ。

  12: VM_LREF_PUSH( 0 )  --- インデックスの値をスタックにプッシュ
  13: VM_IMMVAL_NUM( 1 ) --- 1 をレジスタにロード
  14: VM_ADD2_NUM()      --- インデックスとレジスタの値を加算
  15: VM_LSET( 0 )       --- インデックス更新
  16: VM_LREF_PUSH( 0 )  --- インデックスの値をスタックにプッシュ
  17: VM_LREF( 1 )       --- シーケンスの長さをジレスタにロード
  18: VM_NUMLT2()        --- インデックスとシーケンスの長さを比較
  19: VM_GOTOIFNOT( 28 ) --- 終了条件ならば、ループを抜ける
  20: VM_LREF_PUSH( 2 )  --- シーケンスをスタックにプッシュ
  21: VM_LREF( 0 )       --- インデックスをレジスタにロード
  22: VM_GETITEM()       --- レジスタの値をインデックスとして、スタックにあるシーケンスから値を取り出してレジスタにロード
  23: VM_GSET( i )       --- ループ変数にレジスタの値を代入
 ... ループの中身 ...