実行時に中間コードを機械語へコンパイルして高速化する仕組み。
JITコンパイラとは、プログラムの実行時に、中間コードを機械語へコンパイルして高速化する仕組みです。JITは Just In Time(ちょうど必要なときに)の略で、「動かしている最中に、必要になったところを翻訳する」という意味が込められています。中間コード(=特定のCPUに依存しない中間表現)を、CPUが直接理解できる機械語へ変えるのがポイントです。
身近な例で考えると、同時通訳に似ています。最初は1文ずつその場で訳していきますが(インタプリタ)、何度も同じフレーズが出てくるとあらかじめ訳文を用意しておき、次からはそれをすぐ読み上げます。よく使う部分だけ先に翻訳しておくことで、全体がスムーズになります。
上のツールで▶ボタンを押すと、最初はインタプリタで動いていたプログラムが、よく使う loop 部分を機械語化して速くなっていく流れを確認できます。
実行しながらコンパイルすることには、事前にすべてを翻訳しておく方式にはない利点があります。
・必要な部分だけ高速化:頻繁に実行される部分(ホットスポット)だけを機械語化するので、無駄なコンパイルを避けられる
・実行環境に最適化:実際に動かしている最中の情報(どの分岐をよく通るかなど)を使って、その環境に合った最適化ができる
・起動が速い:最初はインタプリタで動き出すので、コンパイルの待ち時間なしですぐ起動できる
ここでホットスポットとは、プログラムの中でとくに何度も実行される部分のことです。たとえば何万回も回るループは、1回しか実行されない初期化処理よりずっと重要です。JITはこういう「効果の大きい部分」を狙い撃ちして速くします。
よく通る道だけを舗装する道路工事に似ています。すべての道を舗装するのは大変ですが、交通量の多い幹線道路だけを優先して舗装すれば、少ない手間で全体の流れが大きく改善します。Javaの実行環境などで、この仕組みが広く使われています。
コンパイルのタイミングには、JITと対になるAOT(Ahead Of Time=事前に)という考え方もあります。AOTは実行する前にあらかじめ機械語へコンパイルしておく方式です。
・AOT:実行前に事前コンパイル。起動が速いが、最適化は実行前の静的な情報に基づく
・JIT:実行中にコンパイル。起動後に徐々に速くなり、実行時の情報で最適化できる
| 項目 | AOT(事前) | JIT(実行時) |
|---|---|---|
| コンパイル時点 | 実行前 | 実行中 |
| 起動の速さ | 速い | 速い(まずは解釈実行) |
| 最適化 | 静的(実行前の情報のみ) | 実行時情報を使える |
料理にたとえると、AOTは開店前にすべて仕込んでおくイメージで、注文後すぐ出せます。JITは注文を受けてから、よく出る人気メニューだけ作り置きしていくイメージで、営業を続けるほど提供が速くなります。どちらが良いかは、起動速度と実行速度のどちらを重視するかで使い分けます。
JITを理解するには、まずインタプリタとコンパイラの違いを整理すると分かりやすいです。
・インタプリタ(=逐次実行方式):ソースコードを1行ずつ読みながらその場で実行する。待ち時間なしですぐ動き出せるが、毎回読み直すので速度が出にくい
・コンパイラ(=事前変換方式):実行前にまとめて機械語へ変換しておく。変換に時間がかかるが、実行は速い
JITはなぜ両方の良さを得られるのか。秘密は「まずインタプリタで動かして、あとから必要な部分だけコンパイルする」という2段階の戦略にあります。
・起動時:インタプリタで即座に動き始める(コンパイルの待ち時間なし)
・実行中:よく使われる部分(ホットスポット)を見つけ次第、機械語に変換する
・変換後:以降はコンパイル済みの速い機械語で動く
スポーツの練習に例えると、まず試合に出てみて(インタプリタ)、何度も使う技だけ集中特訓する(JITコンパイル)イメージです。全部を特訓してから出場するのではなく、実戦で必要と分かった技術を後から強化するから効率的なのです。
JITが「動かしながら速くなる」仕組みを、4つのステップで見てみましょう。
・① 起動時:インタプリタがプログラムを1行ずつ解釈して実行する。速度は普通
・② ホットスポット検出:「この部分は何度も呼ばれている」と実行回数を数えて把握する
・③ JITコンパイル:しきい値(基準の回数)を超えた部分を機械語に変換し、保存しておく
・④ 高速実行:次からはコンパイル済みの機械語をそのまま使うので速い
なぜ全部を最初からコンパイルしないのか。プログラムの中には1度しか実行されない部分も多いからです。起動処理や初期化などは使い捨てで、コンパイルしても恩恵が少ない。JITは「実際に何度も使われたという実績のある部分だけ」に絞ってコンパイルするので、コストに見合った高速化ができます。これが、全てを事前コンパイルするAOTには真似できないJITだけの強みです。