Политика встраивания CLR 4.0? (возможно ошибка с MethodImplOptions.NoInlining) - PullRequest
3 голосов
/ 28 марта 2010

Я протестировал некоторые новые функции CLR 4.0 в методе inlining (перекрестная сборка) и нашел некоторые странные результаты:

Сборка ClassLib.dll :

using System.Diagnostics;
using System;
using System.Reflection;
using System.Security;
using System.Runtime.CompilerServices;

namespace ClassLib
{
  public static class A
  {
    static readonly MethodInfo GetExecuting =
      typeof(Assembly).GetMethod("GetExecutingAssembly");

    public static Assembly Foo(out StackTrace stack) // 13 bytes
    {
      // explicit call to GetExecutingAssembly()
      stack = new StackTrace();
      return Assembly.GetExecutingAssembly();
    }

    public static Assembly Bar(out StackTrace stack) // 25 bytes
    {
      // reflection call to GetExecutingAssembly()
      stack = new StackTrace();
      return (Assembly) GetExecuting.Invoke(null, null);
    }

    public static Assembly Baz(out StackTrace stack) // 9 bytes
    {
      stack = new StackTrace();
      return null;
    }

    public static Assembly Bob(out StackTrace stack) // 13 bytes
    {
      // call of non-inlinable method!
      return SomeSecurityCriticalMethod(out stack);
    }

    [SecurityCritical, MethodImpl(MethodImplOptions.NoInlining)]
    static Assembly SomeSecurityCriticalMethod(out StackTrace stack)
    {
      stack = new StackTrace();
      return Assembly.GetExecutingAssembly();
    }
  }
}

Сборка ConsoleApp.exe

using System;
using ClassLib;
using System.Diagnostics;

class Program
{
  static void Main()
  {
    Console.WriteLine("runtime: {0}", Environment.Version);

    StackTrace stack;
    Console.WriteLine("Foo: {0}\n{1}", A.Foo(out stack), stack);
    Console.WriteLine("Bar: {0}\n{1}", A.Bar(out stack), stack);
    Console.WriteLine("Baz: {0}\n{1}", A.Baz(out stack), stack);
    Console.WriteLine("Bob: {0}\n{1}", A.Bob(out stack), stack);
  }
}

Результаты:

runtime: 4.0.30128.1

Foo: ClassLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
   at ClassLib.A.Foo(StackTrace& stack)
   at Program.Main()

Bar: ClassLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
   at ClassLib.A.Bar(StackTrace& stack)
   at Program.Main()

Baz:
   at Program.Main()

Bob: ClassLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
   at Program.Main()

Итак, вопросы:

  • Почему JIT не выполняет Foo и Bar звонки, как Baz? Они меньше 32 байтов IL и являются хорошими кандидатами для встраивания.
  • Почему встроенный вызов JIT Bob и внутренний вызов SomeSecurityCriticalMethod помечены атрибутом [MethodImpl(MethodImplOptions.NoInlining)]?
  • Почему GetExecutingAssembly возвращает действительную сборку, когда она вызывается встроенными методами Baz и SomeSecurityCriticalMethod? Я ожидаю, что он выполняет обход стека, чтобы обнаружить выполняющуюся сборку, но стек будет содержать только вызов Program.Main() и никаких методов от ClassLib, соответственно, до ConsoleApp должно быть возвращено.

1 Ответ

3 голосов
/ 04 апреля 2010

В CLR 4.0 для большинства вещей есть события ETW, а вот трассировка ETW для Jit , которая должна указывать причину MethodJitInliningFailed, а здесь - это способ просмотра информации трассировки

...