在主方法中,方法必须有多小才能被内联?

时间:2009-09-28 18:32:31

标签: .net clr

如果我像这样创建一个名为xaisoft.dll的dll:

Using System;
Using System.Reflection;

[assembly:AssemblyVersion("1.0.0.0")]
public class XaiSoft
{
    public string PrintName()
    {
        return "XaiSoft";
    }
}

然后用

编译它
csc /t:library lib.cs

然后创建一个进程程序集以使用我的库:

Using System;
Using System.Reflection;

class App
{
    static void Main()
    {
        ShowAssemblies("Before creating type");
        ShowName();
        ShowAsseblies("After creating type");
    }

    static void ShowName();
    {
        XaiSoft x = new XaiSoft();
        Console.WriteLine("Name: {0}", x.PrintName());
    }

    static void ShowAssemblies(string message)
    {
        Console.WriteLine(message);
        foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
        {
            Console.WriteLine(a.FullName);
        }
     }
}

现在,如果我用csc app.cs /r:XaiSoft.dll

编译上面的代码

然后运行它

我甚至会在调用XaiSoft的PrintName之前看到XaiSoft.dll被加载。

这让我想到了我的问题:

我读到它之所以这样做是因为当运行时JIT编译该方法时,它会通过当前方法(Main)内联调用小方法。这些方法必须有多小才能被内联?并且通过内联来表示它是否提取ShowName的内容并将其直接放在Main方法中。

我还读到,一旦一个方法被JIT编译,它被缓存在内存中,我对此有点困惑。这只是在您的应用程序正常运行时?如果我将其关闭并再次运行,是否必须再次JIT编译我的方法?

有人可以解释该语句:要成功JIT编译方法,运行时必须能够访问方法使用的类型,因此运行时将加载包含这些类型的程序集。回答前面的语句,运行时将使用包含这些类型的程序集,因此它可以jit编译该方法,或者将访问修饰符更改为private以防止它被jit编译

此外,如果编译器足够小,编译器会在调用它们的方法中保持内联方法:

例如,如果我有以下代码段:

   static void Main()
   {
      ShowName();
   }

   static void ShowName()
   {
      ShowAge();
   }

   static void ShowAge()
   {
       ShowGender();
   }

   static void ShowGender()
   {
     //Show gender code
   }

主内联ShowName,ShowName内联ShowAge,ShowAge内联ShowGender吗?如果它确实这样做了,它会更进一步,并且最终会主要内容。

4 个答案:

答案 0 :(得分:2)

我想指出,不应该编写依赖于对CLR抖动如何运作的任何期望的代码。首先,JIT编译器中使用的算法不断发展,并且很可能在未来发生变化。此外,抖动通常处于更好的位置,以决定是否应执行内联等优化。

JIT代码只适用于单个进程(不是跨进程),也不是以任何方式(我知道)在程序执行之间缓存。 JIT编译器需要访问在方法中使用的任何类型才能编译它 - 这包括公共和私有方法。

您可以使用方法上的MethodImplAttribute(MethodImplOptions.NoInlining)属性禁止内联;但是,你不能强迫内联。

答案 1 :(得分:1)

根据Eric Gunnerson,该方法必须小到足以容纳32个字节的IL。

我认为,除了最后一个标准外,其中列出的标准仍然有效。 .NET 3.5sp1现在允许将结构作为参数的方法内联。目前的清单是:

  • 不会内联大于32字节IL的方法。
  • 虚拟函数未内联。
  • 具有复杂流量控制的方法不会内联。复杂流量控制是除if / then / else之外的任何流量控制;在这种情况下,切换或while。
  • 不会内联包含异常处理块的方法,但抛出异常的方法仍然是内联的候选方法。

关于你的问题:

  

主内联ShowName,ShowName内联ShowAge,ShowAge内联ShowGender吗?

Show***方法很可能全部内联。我不相信Main会被内联,特别是考虑到它将成为> 32字节的IL。

答案 2 :(得分:1)

引用类型时会加载程序集。您的ShowName方法使用类型XaiSoft,因此必须先加载相应的程序集,然后才能创建此类型的实例。

代码是JIT编译并作为编译代码存储在流程中。因此,JIT编译的代码不会在同一个应用程序的两个实例之间共享,也不会保存供以后使用。

编译器可能会内联小方法,但您可以使用MethodImplAttributes.NoInlining属性来阻止它被内联。

答案 3 :(得分:1)

听起来你可能正在寻找一种停止内联的方法。您可以通过将System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)属性应用于方法/属性或构造函数来选择退出内联。