web-dev-qa-db-ja.com

CLR対JIT

JITコンパイラとCLRの違いは何ですか?コードをilにコンパイルし、CLRがそのコードを実行する場合、JITは何をしていますか? CLRへのジェネリックの追加により、JITコンパイルはどのように変更されましたか?

42
Ted Smith

JITはCLRの1つの側面です。

具体的には、元の言語のコンパイラ(Microsoft c#の場合はcsc.exeなど)によって生成されたCIL/MSIL(以下、ILと呼びます)を現在のプロセッサ(および現在のプロセスで公開しているアーキテクチャ)にネイティブのマシンコードに変更する部分です。 、たとえば32/64ビット)。問題のアセンブリがngen'dの場合、JITプロセスは完全に不要であり、CLRはこのコードがなくても問題なくこのコードを実行します。

中間表現からまだ変換されていないメソッドを使用する前に、変換するのはJITの責任です。
正確にwhenJITが開始するのは実装固有であり、変更される可能性があります。ただし、CLR設計では、JITが発生する前に関連するコードが実行されることが義務付けられていますが、JVMは対照的に、別のスレッドでしばらくコードを自由に解釈できますマシンコード表現を作成します。
「通常の」CLRは pre-JITスタブアプローチ を使用します。ここで、メソッドごとに、使用時にのみメソッドがJITコンパイルされます。これには、最初のネイティブメソッドスタブを間接的にして、JITにメソッドをコンパイルし、元の呼び出しを変更して最初のスタブをスキップするように指示することが含まれます。代わりに、現在のコンパクトエディションは、ロード時に型のすべてのメソッドをコンパイルします。

ジェネリックスの追加に対処する。

これは、内部の実装の詳細とは対照的に、セマンティクスの点でIL仕様とJITに対する最後の大きな変更でした。

いくつかの新しいIL命令が追加され、タイプとメンバーを計測するためのメタデータオプションが追加されました。 ILレベルでも制約が追加されました。

JITがジェネリック引数を持つメソッドを(包含クラスを通じて明示的または暗黙的に)コンパイルすると、使用されるタイプごとに異なるコードパス(マシンコード命令)が設定されます。これらの変数は同じセマンティクスを示し、同じスペース(IntPtr.Size)を占有するため、実際にはJITはすべての参照タイプに共有実装を使用します。

各値タイプは、そのために生成された特定のコードを取得し、スタック/ヒープ上の変数のサイズの縮小/増加に対処することがこれの主な理由です。また、メソッド呼び出しの前に制約付きオペコードを発行することにより、非参照型の多くの呼び出しは、メソッドを呼び出すために値をボックス化する必要がありません(この最適化は、一般的でない場合にも使用されます)。これにより、default<T>動作は正しく処理され、Null可能でない値の型が使用されている場合は、Nullとの比較が操作なし(常にfalse)として取り除かれます。

実行時にリフレクションを介してジェネリック型のインスタンスを作成しようとすると、型パラメーターはランタイムによって検証され、制約が渡されることを確認します。これが型システム内で使用されない限り、JITに直接影響しません(可能ではありませんが)。

47
ShuggyCoUk

コードをILにコンパイルすると、実行時に実行されてマシンコードにコンパイルされます。これはJITと呼ばれるものです。

編集、答えをさらに具体化するために(まだ過度に簡略化されています):

Visual StudioでC#コードをコンパイルすると、CLRが理解できるILに変換されます。ILは、CLR上で実行されているすべての言語で同じです(これにより、.NETランタイムで複数の言語と相互運用を使用できるようになりますそれらの間で簡単に)。

実行時に、ILはマシンコード(ユーザーが使用しているアーキテクチャーに固有のもの)に解釈されてから実行されます。このプロセスは、Just In Timeコンパイルまたは略してJITと呼ばれます。必要なILのみがマシンコードに変換されます(マシンコードにコンパイルされると、一度だけ「キャッシュ」されます)。ジャストインタイム実行前に=、つまりJITという名前になります。

これは、C#では次のようになります。

C#コード> C#コンパイラ> IL > .NETランタイム> JITコンパイラ>マシンコード>実行

そして、これはVBのようになります

VBコード> VBコンパイラ> IL > .NETランタイム> JITコンパイラ>マシンコード>実行

そして、最初の2つのステップだけが各言語に固有であり、ILに変換された後のすべてが同じであることがわかるので、前に述べたように、.NETの上で複数の異なる言語を実行できる理由は

73
thr

Jon Skeetが言うように、JITはCLRの一部です。基本的にこれは内部で何が起こっているかです:

  1. ソースコードは、共通中間言語(CIL)と呼ばれるバイトコードにコンパイルされます。
  2. すべてのクラスとすべてのメソッド(およびその他すべての:O)からのメタデータは、結果の実行可能ファイル(dllまたはexe)のPEヘッダーに含まれます。
  3. 実行可能ファイルを作成している場合、PEヘッダーには、実行可能ファイルを実行したときにCLR(共通言語ランタイム)をロードする従来のブートストラップも含まれています。

ここで、実行すると:

  1. ブートストラップはCLRを初期化し(主にmscorlibアセンブリを読み込むことにより)、アセンブリを実行するように指示します。
  2. CLRがメインエントリを実行します。
  3. 現在、クラスにはメソッド関数のアドレスを保持するベクターテーブルがあるため、MyMethodを呼び出すと、このテーブルが検索され、対応するアドレスへの呼び出しが行われます。起動時に、すべてのテーブルのすべてのエントリにJITコンパイラのアドレスが含まれています。
  4. そのようなメソッドの1つが呼び出されると、実際のメソッドの代わりにJITが呼び出され、制御を引き継ぎます。次に、JITはCILコードを適切なアーキテクチャの実際のアセンブリコードにコンパイルします。
  5. コードがコンパイルされると、JITはメソッドベクタテーブルに移動しますそしてアドレスをコンパイルされたコードの1つに置き換えます。これにより、以降のすべての呼び出しでJITが呼び出されなくなります。
  6. 最後に、JITはコンパイルされたコードの実行を処理します。
  7. まだコンパイルされていない別のメソッドを呼び出す場合は、4に戻ってください...など...
29
Jorge Córdoba

JITは基本的にCLRのpartです。ガベージコレクターは別です。あなたが相互運用の責任などをどこに置くかは別の問題であり、私がコメントするのに非常に適格ではないものです:)

26
Jon Skeet

スレッドがかなり古いのはわかっていますが、JITを理解できるような写真に入れてみようと思いました。それはすばらしい本 Jeffrey RitcherによるC#経由のCLR からです。図では、彼が話しているメタデータは、アセンブリの型に関するすべての情報が格納されているアセンブリヘッダーで発行されたメタデータです。

JIT image from CLR via C#

15
Punit Vora

1).netプログラムのコンパイル中に、.netプログラムコードが中間言語(IL)コードに変換されます。

2)プログラムを実行すると、メソッドが呼び出されると、中間言語コードがオペレーティングシステムのネイティブコードに変換されます。これはJIT(ジャストインタイム)コンパイルと呼ばれます。

2
ravindranath