什么时候有资格被CLR内联的方法?

时间:2011-01-11 16:34:53

标签: .net clr jit inlining

我在应用程序中观察到了很多“堆栈内省”代码,这些代码通常隐含地依赖于它们包含的方法而不是被内联的正确性。这些方法通常涉及调用:

  • MethodBase.GetCurrentMethod
  • Assembly.GetCallingAssembly
  • Assembly.GetExecutingAssembly

现在,我发现围绕这些方法的信息非常混乱。我听说运行时不会内联调用GetCurrentMethod的方法,但我找不到任何相关的文档。我曾多次在StackOverflow上看过帖子,例如this one,表示CLR没有内嵌跨程序集调用,但GetCallingAssembly documentation强烈指示其他情况。

还有备受诟病的[MethodImpl(MethodImplOptions.NoInlining)],但我不确定CLR是否认为这是“请求”或“命令”。

请注意,我要求从合同的角度内联资格关于当前JITter的实施由于实施困难而无法考虑方法,或者当评估权衡后,JITter最终选择以内联符合条件的方法。我已阅读thisthis,但他们似乎更关注最后两点(有传递提及MethodImpOptions.NoInlining和“异国情调的IL指令”,但这些似乎表现为启发式而非义务)。

CLR 何时允许内联?

5 个答案:

答案 0 :(得分:23)

这是一个抖动实现细节,x86和x64抖动具有微妙的不同规则。这是在团队成员的博客文章中随意记录的,这些团队成员处理抖动,但团队当然保留更改规则的权利。看起来你已经找到了它们。

其他程序集中的内联方法肯定是受支持的,如果不是这样的话,很多.NET类都会非常糟糕。当您查看为Console.WriteLine()生成的机器代码时,您可以看到它正常工作,当您传递一个简单的字符串时,它经常被内联。要自己查看,需要切换到Release版本并更改调试器选项。工具+选项,调试,常规,取消勾选“在模块加载时抑制JIT优化”。

否则没有充分的理由考虑MethodImpOptions.NoInlining malign,这就是它首先存在的原因。事实上它在.NET框架中故意用于许多调用内部帮助器方法的小型公共方法。它使异常堆栈跟踪更容易诊断。

答案 1 :(得分:6)

尽管汉斯帕森特的回答,here在2004年首先提出了几个提示,并进一步提供了一些更新的信息。它们可能会发生变化,但如果您想使方法符合内联条件,它们确实可以让您了解要查找的内容:

  

JIT不会内联:

     
      
  • 使用MethodImplOptions.NoInlining
  • 标记的方法   
  • 大于32字节IL的方法
  •   
  • 虚拟方法
  •   
  • 将大值类型作为参数的方法
  •   
  • MarshalByRef类的方法
  •   
  • 复杂流程图的方法
  •   
  • 满足其他更奇特标准的方法
  •   

特别是MethodImplOptions.AggressiveInlining,它应该可以解除32个字节的限制(或者不管现在和平台发生什么)。

.Net 3.5增加了启发式方法,帮助它确定是否To Inline or not to Inline,这可能是一件好事,尽管这会让开发人员更难预测抖动的决定:

文章引用:

  
      
  1. 如果内联使代码变小,那么它取代的调用就会很好。请注意,我们讨论的是NATIVE代码大小,而不是   IL代码大小(可能完全不同)。

  2.   
  3. 执行特定呼叫站点的次数越多,它就越有利于inlning。因此,循环中的代码应该更多地内联   而不是循环中的代码。

  4.   
  5. 如果内联公开了重要的优化,那么内联是更理想的。特别是具有值类型参数的方法   由于这样的优化,因此比正常受益更多   偏向内联这些方法是好的。

  6.         

    因此,给定内联的X86 JIT编译器使用的启发式算法   候选者。

         
        
    1. 如果方法未内联,请估算调用网站的大小。

    2.   
    3. 估计呼叫站点的大小(如果它是内联的)(这是基于IL的估计,我们使用简单的状态机(Markov)   模型),使用大量实际数据创建,形成此估算逻辑)

    4.   
    5. 计算乘数。默认情况下为1

    6.   
    7. 如果代码处于循环中,则增加乘数(当前启发式循环将其变为5)

    8.   
    9. 如果看起来结构优化会起作用,请增加乘数。

    10.   
    11. 如果InlineSize< = NonInlineSize * Multiplier执行内联。

    12.   

答案 2 :(得分:3)

虽然Hans' answer是正确的,但有一个遗漏,不一定是关于某个方法何时符合内联条件,但是当某个方法时。

Abstract and virtual methods are not eligible for inlining in the CLR

重要的是要注意,因为它会缩小 方法的内联条件。

答案 3 :(得分:1)

有关此线程http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html上的MethodBase.GetCurrentMethod内联的更多信息

大量释义,它声明RefCrawlMark不会停止内联的调用方法。但是,RequireSecObject确实会阻止调用者内联。

此外,Assembly.GetCallingAssembly和Assembly.GetExecutingAssembly方法没有此属性。

答案 4 :(得分:0)

2003年在MSDN上发表了一篇名为Writing High-Performance Managed Apps的文章,其中很清楚地涵盖了几个标准:

  • 大于32个IL字节的方法将不会被内联。
  • 虚拟函数未内联。
  • 具有复杂流控制的方法将不会内联。复杂流控制是除if / then / else之外的任何流控制;在这种情况下,请切换或同时按下。
  • 包含内联异常处理块的方法未内联,尽管引发异常的方法仍是内联的候选对象。
  • 如果该方法的任何形式参数都是struct,则该方法将不会被内联。

Sacha Goldshtein在2012年的aggressive inlining in the CLR上发表的博客文章也有很多相同的建议。