JITコンパイラは通常のコンパイラとどう違うのですか?


22

Java、Ruby、Pythonなどの言語用のJITコンパイラーについては、多くの誇大宣伝がありました。JITコンパイラはC / C ++コンパイラとどのように違いますか。また、C / C ++コンパイラは単にコンパイラと呼ばれますが、Java、Ruby、またはPython用に記述されたコンパイラはなぜJITコンパイラと呼ばれますか?

回答:


17

JITコンパイラは、実行の直前、またはすでに実行中であっても、コードをオンザフライでコンパイルします。このように、コードが実行されているVMは、コード実行のパターンをチェックして、実行時情報でのみ可能な最適化を許可できます。さらに、VMは、コンパイルされたバージョンが何らかの理由(たとえば、多すぎるキャッシュミス、または特定の例外を頻繁にスローするコード)で十分ではないと判断した場合、別の方法で再コンパイルすることを決定し、よりスマートになりますコンパイル。

一方、CおよびC ++コンパイラは伝統的にJITではありません。開発者のマシンで1回だけシングルショットでコンパイルされ、実行可能ファイルが生成されます。


キャッシュミスを追跡し、それに応じてコンパイル戦略を調整するJITコンパイラはありますか?@Victor
マーティンバーガー

@MartinBerger利用可能なJITコンパイラがそれを行うかどうかはわかりませんが、なぜですか?コンピューターがより強力になり、コンピューターサイエンスが発展するにつれて、人々はプログラムにより多くの最適化を適用できます。Javaが誕生した例えば、それがコンパイルされたものに比べて非常に遅い、多分20回今、パフォーマンスはそれほど遅れず、いくつかのコンパイラに匹敵することがある
phuclv

これは昔のブログ投稿で聞いたことがあります。コンパイラは、現在のCPUによって異なる方法でコンパイルされる場合があります。たとえば、CPUがSSE / AVXをサポートしていることを検出した場合、一部のコードを自動的にベクトル化できます。新しいSIMD拡張機能が利用可能になると、新しいSIMD拡張機能にコンパイルされるため、プログラマは何も変更する必要がありません。それとも、それは大きなキャッシュを利用するか、キャッシュミスを減らすように操作/データを配置することができた場合
phuclv

よろしくお願いします!私はそれを信じてうれしいですが、違いは、たとえばCPUタイプやキャッシュサイズは静的なものであり、計算全体で変化しないため、静的コンパイラでも簡単に実行できることです。キャッシュミスOTOHは非常に動的です。
マーティンバーガー14年

12

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が多くのことを行うのが難しくなり、最終的には素朴にコンパイルされたコードがより速く実行されると言われました。図を移動します。


ところで、型消去(Javaのジェネリックなど)が存在する場合、JITは実際には型に対して不利です。
ラファエル

そのため、ランタイム環境は最適化するためにデータを収集する必要があるため、ランタイムはコンパイルされた言語の場合よりも複雑です。
-saadtaame

2
多くの場合、JITは、ゼロ以外で呼び出されることはないことを証明することはできませんが、置き換えることができることを証明することさえできないため、mチェックしなかったバージョンに置き換えるkことはできませんmでそれ。mkstatic miss_count; if (k==0) return a.length+b.length-7; else if (miss_count++ < 16) { ... unoptimized code for ...} else { ... consider other optimizations...}
supercat

1
@supercatに同意し、あなたの例は実際にはかなり誤解を招くと思います。入力または環境の関数ではないことを意味するk=0 alwaysの場合のみ、テストをドロップしても安全ですが、これを決定するには静的分析が必要です。これは非常にコストがかかるため、コンパイル時にのみ手頃な価格です。JITは、コードブロックの1つのパスが他のパスよりもはるかに頻繁に使用され、このパスに特化したバージョンのコードがはるかに高速である場合に勝つことができます。ただし、JITは高速パスが適用されるかどうかを確認するテストを発行し、適用されない場合は「低速パス」を使用します。
j_random_hacker

例:関数はBase* pパラメーターを受け取り、それを介して仮想関数を呼び出します。ランタイム分析は、常に(またはほぼ常に)指している実際のオブジェクトがDerived1タイプのように見えることを示しています。JITは、静的に解決された(またはインライン化された)Derived1メソッド呼び出しを使用して、新しいバージョンの関数を生成できます。このコードの前に、pのvtableポインターが予想されるDerived1テーブルを指しているかどうかをチェックする条件があります。そうでない場合は、代わりに動的に解決されるメソッド呼び出しが遅くなる関数の元のバージョンにジャンプします。
j_random_hacker
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.