回答:
JITコンパイラは、実行の直前、またはすでに実行中であっても、コードをオンザフライでコンパイルします。このように、コードが実行されているVMは、コード実行のパターンをチェックして、実行時情報でのみ可能な最適化を許可できます。さらに、VMは、コンパイルされたバージョンが何らかの理由(たとえば、多すぎるキャッシュミス、または特定の例外を頻繁にスローするコード)で十分ではないと判断した場合、別の方法で再コンパイルすることを決定し、よりスマートになりますコンパイル。
一方、CおよびC ++コンパイラは伝統的にJITではありません。開発者のマシンで1回だけシングルショットでコンパイルされ、実行可能ファイルが生成されます。
JITはジャストインタイムコンパイラの略で、名前はmissonです。実行時に、価値のあるコードの最適化を決定して適用します。通常のコンパイラに代わるものではありませんが、インタープリターの一部です。中間コードを使用するJavaなどの言語には、ソースから中間コードへの変換用の通常のコンパイラーと、パフォーマンス向上のためにインタープリターに含まれるJITの両方があることに注意してください。
コードの最適化は確かに「古典的な」コンパイラで実行できますが、主な違いに注意してください。JITコンパイラは実行時にデータにアクセスできます。これは大きな利点です。それを適切に悪用することは、明らかに難しいかもしれません。
たとえば、次のようなコードを検討してください。
m(a : String, b : String, k : Int) {
val c : Int;
switch (k) {
case 0 : { c = 7; break; }
...
case 17 : { c = complicatedMethod(k, a+b); break; }
}
return a.length + b.length - c + 2*k;
}
通常のコンパイラはこれについてあまり多くのことをすることはできません。ただし、JITコンパイラは、何らかの理由m
でのみ呼び出されることを検出する場合がありますk==0
(コードは時間の経過とともに変化する可能性があります)。次に、より小さなバージョンのコードを作成できます(概念的にはこれをマイナーポイントと見なしますが、ネイティブコードにコンパイルします)。
m(a : String, b : String) {
return a.length + b.length - 7;
}
この時点で、メソッド呼び出しは今では簡単なので、おそらくインライン化さえするでしょう。
どうやら、Sun javac
はJava 6で行われていたほとんどの最適化を却下したようです。これらの最適化により、JITが多くのことを行うのが難しくなり、最終的には素朴にコンパイルされたコードがより速く実行されると言われました。図を移動します。
m
チェックしなかったバージョンに置き換えるk
ことはできませんmでそれ。m
k
static miss_count; if (k==0) return a.length+b.length-7; else if (miss_count++ < 16) { ... unoptimized code for
...} else { ... consider other optimizations...}
k=0
alwaysの場合のみ、テストをドロップしても安全ですが、これを決定するには静的分析が必要です。これは非常にコストがかかるため、コンパイル時にのみ手頃な価格です。JITは、コードブロックの1つのパスが他のパスよりもはるかに頻繁に使用され、このパスに特化したバージョンのコードがはるかに高速である場合に勝つことができます。ただし、JITは高速パスが適用されるかどうかを確認するテストを発行し、適用されない場合は「低速パス」を使用します。
Base* p
パラメーターを受け取り、それを介して仮想関数を呼び出します。ランタイム分析は、常に(またはほぼ常に)指している実際のオブジェクトがDerived1
タイプのように見えることを示しています。JITは、静的に解決された(またはインライン化された)Derived1
メソッド呼び出しを使用して、新しいバージョンの関数を生成できます。このコードの前に、p
のvtableポインターが予想されるDerived1
テーブルを指しているかどうかをチェックする条件があります。そうでない場合は、代わりに動的に解決されるメソッド呼び出しが遅くなる関数の元のバージョンにジャンプします。