プロファイル API でのコード生成
このトピックでは、Microsoft Intermediate Language (MSIL) コードからネイティブ コードへのフローと、プロファイラーによるコード生成の制御方法について説明します。
自動コード生成と手動コード生成
.NET Framework アセンブリ内の MSIL は、次のいずれかの方法でネイティブ コードにコンパイルできます。
手動コンパイル : ネイティブ イメージ ジェネレーター (NGen.exe) ツールを使用して、ネイティブ イメージを作成します。
自動コンパイル: 共通言語ランタイム (CLR: Common Language Runtime) が実行時に Just-In-Time (JIT) コンパイルを実行します。
NGen.exe と JIT コンパイラのどちらにも、コード生成を制御するためのフラグが用意されています。
アセンブリが読み込まれると、CLR は、まずアセンブリのネイティブ イメージを検索します。 適切なコード生成フラグが設定されたネイティブ イメージが見つからない場合、CLR は、実行中に必要に応じてアセンブリ内の関数を JIT コンパイルします。 ネイティブ イメージが見つかって読み込まれた場合でも、CLR は、アセンブリ内の関数の一部を JIT コンパイルすることがあります。
プロファイラーによるコード生成の制御
プロファイラーは、次の表に示すフラグを使用してコード生成を制御します。
フラグ |
効果 |
---|---|
COR_PRF_USE_PROFILE_IMAGES |
(.NET Framework Version 2.0 が必要) ネイティブ イメージ検索では、プロファイラーが拡張したイメージ (ngen /profile) を検索します。 指定されたアセンブリについて、プロファイラーが拡張したネイティブ イメージの検索に失敗した場合、CLR は、そのアセンブリ内のメソッドを必要に応じて JIT コンパイルします。 JIT コンパイル済みのコードには影響しません。 |
COR_PRF_DISABLE_INLINING |
ネイティブ イメージ検索には影響しません。 JIT コンパイルでは、インライン展開を無効にします。 他のすべての最適化は有効です。 |
COR_PRF_DISABLE_OPTIMIZATIONS |
ネイティブ イメージ検索には影響しません。 JIT コンパイルでは、インライン展開を含むすべての最適化を無効にします。 |
COR_PRF_MONITOR_ENTERLEAVE |
ネイティブ イメージ検索では、プロファイラーが拡張したイメージ (ngen /profile) を検索します。 JIT コンパイルでは、生成後のコードに enter および leave のフックを挿入します。 |
COR_PRF_MONITOR_CODE_TRANSITIONS |
ネイティブ イメージ検索では、プロファイラーが拡張したイメージ (ngen /profile) を検索します。 JIT コンパイルでは、マネージとアンマネージの移行ポイントにフックを挿入します。 |
プロファイラーとネイティブ イメージ
NGen.exe は、ネイティブ イメージを作成するときに、CLR が実行時に通常実行する作業の大半 (クラスの読み込みや関数のコンパイルなど) を実行します。 その結果、NGen 時に作業が行われた場合は、次のプロファイラー コールバックが実行時に受け取られなくなります。
プロファイラーが拡張したネイティブ イメージ
NGen.exe を使用してネイティブ イメージを作成すると、イメージのプロファイリングを容易にする一連のコード生成フラグがオンになります。
enter および leave のフックがコードに挿入されます。
マネージとアンマネージの移行フックがコードに挿入されます。
ネイティブ イメージ内の各関数が初めて呼び出されるたびに、ICorProfilerCallback::JITCachedFunctionSearchStarted 通知と ICorProfilerCallback::JITCachedFunctionSearchFinished 通知が発行されます。
ネイティブ イメージ内の各クラスが初めて使用されるたびに、ICorProfilerCallback::ClassLoadStarted 通知と ICorProfilerCallback::ClassLoadFinished 通知が発行されます。
パフォーマンスに関する考慮事項
NGen で生成されたアプリケーションをプロファイリングするために使用する方法は、2 つの考慮事項によって決まります。つまり、取得する必要があるデータと、アプリケーションに埋め込まれたプロファイル フックの効果に依存します。
このトピックで既に説明したように、NGen のプロファイリングには、次の 2 つの基本的なシナリオがあります。
通常のネイティブ イメージ (プロファイル フックがない NGen 生成イメージ)。これらのイメージでは、プロファイリングのオーバーヘッドは発生しません。 ただし、通常のネイティブ イメージでは、クラスの読み込み時とアンロード時のコールバックは使用できません。 この状況に対応するために、プロファイラーが拡張したネイティブ イメージを要求しないプロファイラーでは、FunctionID や ClassID の検出時に、それらの ID に関する情報を収集する必要があります。 たとえば、サンプリング プロファイラーでは、初めて JIT コンパイルされたとき、または NGen 生成イメージから初めて読み込まれたときに、FunctionID を検出することはできません。これは、通常の NGen 生成イメージでは、クラスの読み込み時とアンロード時のコールバックが発行されないためです。 このプロファイラーで FunctionID が検出されるのは、関数のコンパイル済みのコード本体に含まれる命令ポインター (IP) でプロセスが実行されたことがサンプリングによって判明したときです。 この場合、プロファイラーは、関数についての情報をサンプリング中に問い合わせることも、FunctionID や関連するメタデータ トークンを記録して後で問い合わせることもできます。 したがって、プロファイラーは、FunctionID や ClassID が生成された時点ではなく、ID が実際に使用される最後の時点で初めて、ID に関する情報を問い合わせることになります。
プロファイラーが拡張したネイティブ イメージ (プロファイル フックが埋め込まれた NGen 生成イメージ)。これらのイメージはサイズが大きく、通常のイメージとは大幅に異なります。 さらに、プロファイル フックが含まれていると、アプリケーションの動作が異なる場合があります。 したがって、プロファイラーが拡張したネイティブ イメージは、パフォーマンスと動作に関する影響の可能性 (オーバーヘッド) を容認できる場合にのみ使用してください。