5

MSDNで概説されてAssembly.GetCallingAssembly()いるJITインライン化による動作の変化を示す短いC#スニペットを作成しようとしています。これまでの私のコードは次のとおりです。

class Program
{
   static void Main(string[] args)
   {
       Console.WriteLine( GetAssembly().FullName );
       Console.ReadLine();
   }

   static Assembly GetAssembly()
   {
       return System.Reflection.Assembly.GetCallingAssembly();
   }
}

これを「リリース」に組み込み、「デバッグなしで開始」を使用し始めます。このセットアップにより、この回答からのコードがインライン化されました。私が見る結果は

ConsoleApplication2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

明らかに、にGetAssembly()インライン化されていませんMain()。そうでない場合はmscorlib、呼び出し元のアセンブリと見なされます。

すべてのインライン化基準を確認しましたが、なぜインライン化されないのかわかりませんGetAssembly()

なぜJITコンパイラが呼び出しをインライン化しないことにしたのかを正確に知ることはどういうわけか可能ですか?

4

3 に答える 3

8

これは、.NET 3.5でのAssembly.GetCallingAssembly()の宣言です。

[MethodImpl(MethodImplOptions.NoInlining)]
public static Assembly GetCallingAssembly()
{
    StackCrawlMark lookForMyCallersCaller = StackCrawlMark.LookForMyCallersCaller;
    return nGetExecutingAssembly(ref lookForMyCallersCaller);
}

StackCrawlMark列挙型は興味深いものです。発信者がインライン化される場合、「発信者の発信者を探す」はうまく機能しません。列挙型が宣言されるthread.csのSSCLI20ソースコードにコメントがあります。

この列挙型のローカル変数を宣言し、それをrefによってスタッククロールを実行する必要のある関数に渡すと、呼び出し元[sic]のインライン化が防止され、ESPポイントがスタッククロールに渡されます。

これはGetCallingAssembly()で発生することとよく一致します。これはローカル変数であり、実際にrefによって渡されます。メカニズムが何であるかはわかりませんが、ジッターはCORINFO_FLG_BAD_INLINEEという名前のメソッド属性を生成する可能性があります。これにより、MethodDesc :: SetNotInline()が強制的に呼び出されます。それは推測です、これは非常にあいまいです。

于 2012-10-16T10:19:22.353 に答える
3

これに私の2セントを追加します。プログラムを正しく機能させるためにJIT行う可能性のあることに依存しないでください。

JITがメソッドをインライン化できない条件のいくつかは次のとおりです(ここから取得)

  • 32バイトを超えるILのメソッドはインライン化されません。
  • 仮想関数はインライン化されていません。
  • 複雑なフロー制御を持つメソッドはインライン化されません。複雑なフロー制御は、if / then/else以外のフロー制御です。この場合、またはwhileを切り替えます。
  • 例外処理ブロックを含むメソッドはインライン化されませんが、例外をスローするメソッドは依然としてインライン化の候補です。
  • メソッドの正式な引数のいずれかが構造体である場合、メソッドはインライン化されません。

JITメソッドをインライン化できるからといって、必ずしもそうなるとは限りません。それをビルド構成/ランタイム/OSの組み合わせ間の動作の違いと組み合わせると、まあ...そこにあります。

.Net 3.5 SP1 JITのインライン化動作の詳細については、こちらをご覧ください

于 2012-10-16T08:46:00.323 に答える
1

実際、への呼び出しProgram.GetAssembly はにインライン化されましProgram.Mainた。Program.GetAssemblyただし、とProgram.Mainは同じ名前のアセンブリで定義されているため、違いはわかりませんConsoleApplication2

別の方法でJITインライン化を説明できますが、次のようになります。

using System;
using System.Diagnostics;

namespace A3
{
    public class Program
    {
        static void Main(string[] args)
        {
            StackFrame[] stackFrames = GetStackFrames();
            foreach (StackFrame stackFrame in stackFrames)
                Console.WriteLine(stackFrame.GetMethod().Name); // write method name

            Console.ReadLine();
        }

        //[MethodImpl(MethodImplOptions.NoInlining)]
        static StackFrame[] GetStackFrames()
        {
            StackTrace stackTrace = new StackTrace(); // get call stack
            return stackTrace.GetFrames(); // get method calls (frames)
        }
    }
}

JITインライン化がない場合(たとえば、デバッグモードの場合、または[MethodImpl(MethodImplOptions.NoInlining)]属性がに適用されている場合GetStackFrames)、コンソールに少なくとも2行が書き込まれます。

  1. GetStackFrames
  2. Main

ただし、インライン化が発生した場合、stackFrames含まれるメソッドは1つだけです。Main

アップデート

また、ここで読むことができるように:デバッグとホスティングプロセス、およびローリングが彼のコメントで述べたように:

Assembly.GetCallingAssembly()。FullNameは、ホスティングプロセスが有効になっているかどうかに応じて異なる結果を返します。ホスティングプロセスを有効にしてAssembly.GetCallingAssembly()。FullNameを呼び出すと、mscorlibが返されます。ホスティングプロセスを無効にしてAssembly.GetCallingAssembly()。FullNameを呼び出すと、アプリケーション名が返されます。

于 2012-10-16T08:37:16.800 に答える