概要
インタプリタ言語用のXは、任意のプログラムの実行プログラム(またはマシン、または一般に機構のちょうどいくつかの種類)であるP言語で記述されたXを、それが効果を行うの仕様によって規定されるような結果を評価するようにXを。CPUは通常、それぞれの命令セットのインタープリターですが、最新の高性能ワークステーションCPUは実際にはそれよりも複雑です。実際には、基礎となる独自のプライベート命令セットがあり、外部から見えるパブリック命令セットを翻訳(コンパイル)または解釈する場合があります。
コンパイラからXのYは、任意のプログラム変換プログラム(またはマシン、または一般に機構のちょうどいくつかの種類)であるPいくつかの言語からXを意味的に等価なプログラムにP '一部の言語におけるYようにその意味論つまり、Yのインタープリターでp 'を解釈すると、Xのインタープリターでpを解釈するのと同じ結果が得られ、同じ効果が得られます。(XとYは同じ言語である可能性があることに注意してください。)
用語控え・オブ・タイム(AOT)とジャスト・イン・タイム(JIT)を参照するとき、コンパイルが行われます。これらの用語でいう「時間」は、「ランタイム」である、すなわちJITコンパイラは、プログラムをコンパイルすることがあるとして実行中、AOTコンパイラは実行前にプログラムをコンパイルします。これは、言語からJITコンパイラている必要がありますXの言語にYが何らかの形で言語の通訳と一緒に働かなければならないYそうでなければ、プログラムを実行する方法はありません。(たとえば、JavaScriptをx86マシンコードにコンパイルするJITコンパイラは、x86 CPUなしでは意味がありません。実行中にプログラムをコンパイルしますが、x86 CPUなしではプログラムは実行されません。)
この区別はインタープリターにとって意味をなさないことに注意してください。インタープリターがプログラムを実行します。実行前にプログラムを実行するAOTインタープリターや、実行中にプログラムを実行するJITインタープリターのアイデアは無意味です。
だから、私たちは持っています:
- AOTコンパイラー:実行する前にコンパイルします
- JITコンパイラー:実行中にコンパイルします
- インタープリター:実行
JITコンパイラー
JITコンパイラファミリ内では、正確にコンパイルするタイミング、頻度、および粒度について多くの違いがあります。
たとえば、MicrosoftのCLRのJITコンパイラは、コードを1回だけ(コンパイル時に)コンパイルし、一度にアセンブリ全体をコンパイルします。他のコンパイラーは、プログラムの実行中に情報を収集し、新しい情報が利用可能になったときにコードを数回再コンパイルして、最適化できるようにします。一部のJITコンパイラーは、コードの最適化を解除することもできます。さて、なぜあなたはそれをしたいのかと自問するかもしれません。最適化を解除すると、実際には安全ではない可能性のある非常に積極的な最適化を実行できます:積極的すぎると判明した場合は、再度元に戻すことができますそもそも積極的な最適化。
JITコンパイラは、一度にコードの一部静的単位をコンパイルすることができるいずれかの(一つのモジュール、一つのクラス、一つの機能、一つの方法、...、これらの一般的に呼ばれている方法アット時間、例えば、JIT)又はそれらがよいトレース動的にコードを実行して、動的トレース(通常はループ)を見つけてコンパイルします(これらはトレース JIT と呼ばれます)。
通訳者とコンパイラの組み合わせ
通訳者とコンパイラは、単一の言語実行エンジンに結合できます。これが行われる典型的なシナリオは2つあります。
AOTコンパイラの組み合わせXとYのための通訳とY。ここで、通常、Xは人間が読みやすいように最適化された上位言語であり、Yはは、マシンによる解釈が可能なように最適化されたコンパクトな言語(多くの場合、ある種のバイトコード)です。たとえば、CPython Python実行エンジンには、PythonソースコードをCPythonバイトコードにコンパイルするAOTコンパイラーと、CPythonバイトコードを解釈するインタープリターがあります。同様に、YARV Ruby実行エンジンには、RubyソースコードをYARVバイトコードにコンパイルするAOTコンパイラーと、YARVバイトコードを解釈するインタープリターがあります。なぜそれをしたいのですか?RubyとPythonはどちらも非常に高レベルでやや複雑な言語であるため、まず解析しやすく解釈しやすい言語にコンパイルしてから、その言語を解釈します。
インタープリターとコンパイラーを組み合わせるもう1つの方法は、混合モードの実行エンジンです。ここでは、同じ言語を実装する2つの「モード」、つまりXのインタープリターとXからYへのJITコンパイラーを「ミックス」します。(つまり、ここでの違いは、上記の場合、コンパイラがプログラムをコンパイルし、結果をインタープリターに渡す複数の「ステージ」があったことです。ここでは、同じ言語で2つのサイドバイサイドが動作します。 )コンパイラーによってコンパイルされたコードは、インタープリターによって実行されるコードよりも速く実行される傾向がありますが、実際には最初にコードをコンパイルするのに時間がかかります(特に、実行するコードを大幅に最適化する場合本当に速い、それがかかるたくさん)の時間を。したがって、JITコンパイラーがコードのコンパイルで忙しいこの時間を埋めるために、インタープリターはすでにコードの実行を開始でき、JITのコンパイルが完了したら、コンパイル済みコードに実行を切り替えることができます。これは、コンパイルされたコードの可能な限り最高のパフォーマンスが得られることを意味しますが、コンパイルが完了するのを待つ必要はなく、アプリケーションはすぐに実行を開始します(可能な限り高速ではありません)。
これは、実際には混合モード実行エンジンの可能な限り単純なアプリケーションです。より興味深い可能性は、たとえば、すぐにコンパイルを開始せずに、インタプリタを少し実行して、統計、プロファイリング情報、型情報、特定の条件分岐がとられる可能性に関する情報、どのメソッドが呼び出されるかを収集することですほとんどの場合、この動的な情報をコンパイラに送り、より最適化されたコードを生成できるようにします。これは、上で説明した非最適化を実装する方法でもあります。最適化に積極的すぎることが判明した場合は、コード(の一部)を破棄して解釈に戻すことができます。たとえば、HotSpot JVMはこれを行います。JVMバイトコード用のインタープリターとJVMバイトコード用のコンパイラーの両方が含まれています。(実際には、2つのコンパイラ!)
また、これら2つのアプローチを組み合わせることも可能です。実際には、2つのフェーズはXをYにコンパイルするAOTコンパイラであり、2番目のフェーズはYを解釈しYをZにコンパイルする混合モードエンジンです。たとえば、Rubinius Ruby実行エンジンはこのように動作します。RubyソースコードをRubiniusバイトコードにコンパイルするAOTコンパイラーと、Rubiniusバイトコードを最初に解釈し、情報を収集すると最も頻繁に呼び出されるメソッドをネイティブにコンパイルする混合モードエンジンを備えていますマシンコード。
混合モード実行エンジンの場合、つまり高速起動を提供し、潜在的に情報を収集し、フォールバック機能を提供する場合にインタープリターが果たす役割は、別のJITコンパイラーによっても果たされることに注意してください。これは、たとえばV8の動作方法です。V8は解釈することはなく、常にコンパイルします。最初のコンパイラは、非常に高速で非常にスリムなコンパイラで、非常に高速に起動します。ただし、生成されるコードは非常に高速ではありません。また、このコンパイラは、生成するコードにプロファイリングコードを挿入します。他のコンパイラーはより遅く、より多くのメモリを使用しますが、より高速なコードを生成し、最初のコンパイラーによってコンパイルされたコードを実行することにより収集されたプロファイル情報を使用できます。