JITの入門として、Eli Bendersky氏の記事 Adventures in JIT compilation: Part 1, Part 2 (日本語訳1, 2) を読んでみた。
パート1
この記事ではBrainf*ckをターゲット言語として使用していて、まずは愚直なインタプリタの実装から始める。
次に最適化として、
- ジャンプ先をあらかじめ計算する(
[
]
) - 同じ命令が連続する場合、1つにまとめる(
<
>
でのポインタの移動、+
-
でのメモリのインクリメント・デクリメント) - よく出てくる命令列を追加命令として実装する(メモリの値を0にする
[-]
、現在のメモリの値を別の場所に加算[<<…< + >>…> -]
)
ということを行う。
動作サンプルはマンデルブロ集合と素数の因数分解で、処理時間の計測をする。
パート2
いよいよJIT化。
- 最適化なしのコード(ただしジャンプの飛び先はあらかじめ計算する)を、まずは外部のライブラリを使わずに直接バイナリを出力しての実行
- asmjitを使って、楽になることを示す
- インタプリタ版の最適化を施したJIT版を作成
という順で説明する。
感想
- Brainf*ckは実装の入門言語として素晴らしい:パースが楽、命令が単純で少ない、ループの対応が取れているのでジャンプ先の計算がスタック管理できる、扱う型が数値のみなのでマシン語化しやすい
- サンプルがそこそこ大きいものなので、高速化が体感できてよい
- asmjitの代わりにXbyakに置き換えてもみたけど、割と素直に変更できました
- 元々のJIT版自体、なんかどこかメモリを破壊しているような気がする…(
--verbose
時にコードが出力されたりされなかったりする) - ソース:https://github.com/tyfkda/jittest
しかし、JITとはいっても起動時にすべて変換する方式だとAOTコンパイルとそんなに変わらないなぁと思った。 これを最初はインタプリタで、ホットだと判定したらJITコンパイルするとかするにはどうするんだろうか。