现在的位置: 首页 > 综合 > 正文

.NET(C#):MethodImplOptions.NoInlining来阻止JIT内联方法

2013年05月10日 ⁄ 综合 ⁄ 共 1648字 ⁄ 字号 评论关闭

众所周知,引用程序集的加载并不是在程序开始运行时就全部加载的,CLR会加载相应的程序集当该程序集的类型被第一次使用。更具体的说,当一个方法被JIT时,CLR会确保该方法中的类型所在的程序集被加载。

 

比如我们的主程序Mgen.exe引用一个类库ClassLibrary1,后者有一个类型Class1:

image

 

这段代码,分别在使用ClassLibrary1的一个类型之前和之后对当前应用程序域加载的程序集进行枚举:

static void Main()

{

    foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())

        Console.WriteLine(ass.GetName().Name);

 

    Console.WriteLine("=== 分割线 ===");

 

    //使用ClassLibrary1的类型

    doo();

 

    foreach (var ass in AppDomain.CurrentDomain.GetAssemblies())

        Console.WriteLine(ass.GetName().Name);

 

}

 

static void doo()

{

    var obj = new ClassLibrary1.Class1();

}

 

运行代码(注意要在Release发布模式),程序会输出:

mscorlib

Mgen

ClassLibrary1

=== 分割线 ===

mscorlib

Mgen

ClassLibrary1

 

为何ClassLibrary1出现在第一个列表中?事实上如果JIT不对doo进行内联处理的话,当Main方法被JIT后,CLR是不会觉察到Main方法会使用ClassLibrary1的,因此ClassLibrary1是不会出现在列表中的,但运行时刻JIT的优化使得doo被内联在Main方法中,这样整个代码相当于:

static void Main()

{

    //省略

 

    //内联的doo

    var obj = new ClassLibrary1.Class1();

 

    //省略

}

 

使用MethodImplOptions.NoInlining可以阻止JIT在编译时把某些方法进行内联处理。注意MethodImpleOptions枚举要用在MethodImpl特性上。(另外MethodImpleOptions枚举还有其他功能,比如常见的MethodImpleOptions.Synchronized用来进行线程同步。可以参考MSDN:http://msdn.microsoft.com/zh-cn/library/system.runtime.compilerservices.methodimploptions.aspx

最后注意上述类都在System.Runtime.CompilerServices命名空间内。

 

这样的话在doo方法上加入MethodImpl特性:

//+ System.Runtime.CompilerServices;

[MethodImpl(MethodImplOptions.NoInlining)]

static void doo()

{

    var obj = new ClassLibrary1.Class1();

}

 

再次编译整个程序,输出:

mscorlib

Mgen

=== 分割线 ===

mscorlib

Mgen

ClassLibrary1

 

可以看到,此时由于doo没有被内联,JIT编译Main方法后没有发现使用其他程序集,所以第一次枚举程序集没有ClassLibrary1,而当doo方法真正执行后,更确切的说是在doo方法被JIT后,但在执行前,CLR会加载ClassLibrary1。所以第二次枚举应用程序域中的程序集时,ClassLibrary1在列表中。

 

如果在Debug调试模式下运行程序,结果也是上面的输出(ClassLibrary1不在列表1),因为调试模式下,JIT方法内联优化是被禁止的。

Open-mouthed smile

抱歉!评论已关闭.