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

我认为我正在观察.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(); } } 

通过优化上述程序的发布版本为MainEmptyBody生成以下MSIL:

 .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 

EmptyBody ,MSIL包含从MainEmptyBody的调用,因为预期C#编译器不会内联或优化掉这样的调用。 但是,我认为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] 

指令指针立即设置为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 

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

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

 00BD2653 cmp dword ptr ds:[4B00B84h],0 00BD265A je 00BD2661 00BD265C call 73E6AF20 

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

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

取消选中这些

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

在VS>工具>调试>常规下,将显示“真正的”JIT编译结果,在我上面的Main调用EmptyBody是这样的:

 004C2620 ret 

这意味着对EmptyBody的调用被完全删除,这是预期的,这个世界仍然是一个快乐且有些可预测的居住地:)