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

Ani*_*Ani 37 .net clr jit inlining

我在应用程序中观察到了很多"堆栈内省"代码,这些代码通常隐含地依赖于它们的包含方法没有内联的正确性.这些方法通常涉及到:

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

现在,我发现围绕这些方法的信息非常混乱.我听说运行时不会内联调用GetCurrentMethod的方法,但我找不到任何相关的文档.我曾多次在StackOverflow上看过帖子,比如这个帖子,表明CLR没有内联交叉汇编调用,但GetCallingAssembly 文档强烈指出不是这样.

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

请注意,我要求从合同的角度来概述资格,而不是当前JITter的实施由于实施困难而拒绝考虑方法,或者JITter最终在评估交易后最终选择内联合格方法时权衡.我已经阅读了这个这个,但他们似乎更关注最后两点(有传递提到的MethodImpOptions.NoInlining和"异国情调的IL指令",但这些似乎是作为启发而不是义务呈现).

什么时候CLR 允许内联?

Han*_*ant 23

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

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

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


Eug*_*sky 6

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

JIT不会内联:

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

特别是,有一个MethodImplOptions.AggressiveInlining,它应该提升32个字节的限制(或者现在和你的平台发生的任何事情).

.Net 3.5增加了启发式方法,帮助它确定是否内联,这可能是一件好事,尽管它使开发人员更难以预测抖动的决定:

文章的引用:

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

  2. 特定呼叫站点的执行越多,它就越有利于inlning.因此,循环中的代码应该比不在循环中的代码更内联.

  3. 如果内联暴露了重要的优化,那么内联是更理想的.特别是具有值类型参数的方法由于这样的优化而受益于正常,因此具有内联这些方法的偏差是好的.

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

  1. 如果未内联方法,则估计调用站点的大小.

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

  3. 计算乘数.默认情况下为1

  4. 如果代码处于循环中,则增加乘数(当前启发式在循环中将其颠覆为5)

  5. 如果看起来结构优化将会起作用,请增加乘数.

  6. 如果InlineSize <= NonInlineSize*Multiplier执行内联.