HSPをJIT実行
ようやくraytraceが1.7倍速くらいで動くようになった。
http://peppermint.jp/products/hsp/
http://github.com/zakki/openhsp/tree/jit
まんまhsp3cnvと同じだとインタプリタ実行と等速か若干遅いくらいなので、次のあたりを実装した。
- 実行時にトレースしつつ型をチェックする
- 型が一定ならスタック操作を経由せずに四則演算やロード・ストアを直接計算する
- ローカル変数として使われている変数を検出してメモリアクセスをサボる
パフォーマンス的に効きそうな項目も結構残ってる。
完全に変数の型を解析してかつグローバルに最適化かけられれば10倍速くらいで動いてもいいはずなんだよなー
- hsp3cnv由来で関数の引数が値渡しで済むはずの箇所も参照渡ししている
- (関数で無い)コマンドはそのまま
- 自動型変換が必要な箇所は以前hspvarにまかせている
- ベーシックブロック単位でしか処理していない
あと、機能的にユーザー定義関数やモジュールが使えないのが痛い。
HSPの1行が
d = 1.0 / os.hit
次のようなLLVM-IRになって
%ptr = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar43, i32 0, i32 4) ; <i8*> [#uses=1] %0 = bitcast i8* %ptr to i32* ; <i32*> [#uses=1] %1 = load i32* %0 ; <i32> [#uses=1] %ptr1 = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar13, i32 0, i32 4) ; <i8*> [#uses=1] %2 = bitcast i8* %ptr1 to double* ; <double*> [#uses=1] call void @HspVarCoreReset(%struct.PVal* @Var__HspVar13) call void @HspVarCoreArray2(%struct.PVal* @Var__HspVar13, i32 %1) %offset = load i32* getelementptr inbounds (%struct.PVal* @Var__HspVar13, i32 0, i32 8) ; <i32> [#uses=1] %3 = getelementptr double* %2, i32 %offset ; <double*> [#uses=1] %4 = load double* %3 ; <double> [#uses=1] %5 = fdiv double 1.000000e+000, %4 ; <double> [#uses=1] %ptr2 = load i8** getelementptr inbounds (%struct.PVal* @Var__HspVar71, i32 0, i32 4) ; <i8*> [#uses=1] %6 = bitcast i8* %ptr2 to double* ; <double*> [#uses=1] store double %5, double* %6
最終的にx86命令に落ちると
movl _Var__HspVar43+28, %eax movl (%eax), %esi movl _Var__HspVar13+28, %edi movl $_Var__HspVar13, (%esp) call _HspVarCoreReset movl %esi, 4(%esp) movl $_Var__HspVar13, (%esp) call _HspVarCoreArray2 movsd LCPI89_0, %xmm0 movl _Var__HspVar13+40, %eax divsd (%edi,%eax,8), %xmm0 movl _Var__HspVar71+28, %eax movsd %xmm0, (%eax)
Cに比べるとだいぶ無駄がある。ループのフロー見て配列の自動拡張必要ないことを特定できれば真ん中の当たりは消せるな。