为什么.NET JIT编译器决定不内联或优化掉没有副作用的空静态方法的调用?

Joh*_*ell 26 c# clr compiler-optimization

我认为我正在观察.NET JIT编译器没有内联或优化对没有副作用的空静态方法的调用,考虑到一些直言不讳的在线资源,这有点令人惊讶.

我的环境是x64,Windows 8.1,.NET Framework 4.5上的Visual Studio 2013.

鉴于这个简单的测试程序(https://ideone.com/2BRCpC)

class Program
{
    static void EmptyBody()
    {
    }

    static void Main()
    {
        EmptyBody();
    }
}
Run Code Online (Sandbox Code Playgroud)

发布版本与上述程序的最优化产生以下MSIL为MainEmptyBody:

.method private hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  call       void Program::EmptyBody()
  IL_0005:  ret
} // end of method Program::Main

.method private hidebysig static void  EmptyBody() cil managed
{
  // Code size       1 (0x1)
  .maxstack  8
  IL_0000:  ret
} // end of method Program::EmptyBody
Run Code Online (Sandbox Code Playgroud)

这并不奇怪,MSIL包含一个电话MainEmptyBody,因为C#编译器预计不会inline或优化掉这样的电话.但是,我认为JIT编译器会内联或优化掉该调用.但这似乎并没有发生.

如果我运行上面的程序并进入调试器Main,生成的程序集是这样的:

00572621  mov         ebp,esp  
00572623  cmp         dword ptr ds:[4320B84h],0  
0057262A  je          00572631  
0057262C  call        73E6AF20  
00572631  call        dword ptr ds:[4321578h]  
Run Code Online (Sandbox Code Playgroud)

指令指针立即设置为00572631的最后一行,即调用EmptyBody.踏入EmptyBody,发现生成的组件

00BD2651  mov         ebp,esp  
00BD2653  cmp         dword ptr ds:[4B00B84h],0  
00BD265A  je          00BD2661  
00BD265C  call        73E6AF20  
00BD2661  nop  
00BD2662  pop         ebp  
00BD2663  ret
Run Code Online (Sandbox Code Playgroud)

指令指针立即设置为nop00BD2661 的行,它没有做任何事情,我无法猜测为什么它首先生成.

鉴于上面的两个汇编代码段共享相同的4指令头,我假设这只是常规方法入口样板,其中堆栈等设置.我很想学会知道这些重复的指令会做什么,但是:

00BD2653  cmp         dword ptr ds:[4B00B84h],0  
00BD265A  je          00BD2661  
00BD265C  call        73E6AF20  
Run Code Online (Sandbox Code Playgroud)

无论如何,主要的问题是:为什么JIT编译器生成调用空体静态方法的程序集EmptyBody

Joh*_*ell 32

经过深入挖掘后,事实证明我可以自己回答这个问题.正如在http://blogs.msdn.com/b/vancem/archive/2006/02/20/535807.aspx中所解释的那样,在调试器下观察优化版本构建的反汇编将默认影响JIT编译器.

取消选中这些

  • '抑制模块负载的JIT优化'
  • '启用我的代码'

在VS>工具>调试>常规下,将显示"真正的"JIT编译结果,对于EmptyBodyMain上面的调用是这样的:

004C2620  ret
Run Code Online (Sandbox Code Playgroud)

这意味着呼叫EmptyBody完全被删除,这是预期的,世界仍然是一个快乐和有点可预测的居住地:)

  • "nop"是一个死的赠品,正在发生这种情况.如果你看到那些,你知道该怎么做:) (2认同)