我在应用程序中观察到了很多“堆栈内省”代码,这些代码通常隐含地依赖于它们的包含方法没有内联的正确性。这些方法通常涉及到:
MethodBase.GetCurrentMethod
Assembly.GetCallingAssembly
Assembly.GetExecutingAssembly
现在,我发现围绕这些方法的信息非常混乱。我听说运行时不会内联调用GetCurrentMethod的方法,但我找不到任何相关的文档。我曾多次在StackOverflow上看过帖子,比如this one,表明CLR没有内联交叉汇编调用,但GetCallingAssembly
documentation强烈表示不同。
还有备受诟病的[MethodImpl(MethodImplOptions.NoInlining)]
,但我不确定CLR是否认为这是“请求”或“命令”。
请注意,我要求从合同的角度来概述资格,而不是当前JITter的实施由于实施困难而拒绝考虑方法,或者JITter最终在评估交易后最终选择内联合格方法时权衡。我读过this和this,但他们似乎更专注于最后两点(传递提到MethodImpOptions.NoInlining和“异国情调的IL指令”,但这些似乎是作为启发而不是义务呈现)。
什么时候CLR允许内联?
这是一个抖动实现细节,x86和x64抖动具有微妙的不同规则。这是在团队成员的博客文章中随意记录的,这些团队成员处理抖动,但团队当然保留更改规则的权利。看起来你已经找到了它们。
当然,支持来自其他程序集的内联方法,如果不是这样的话,很多.NET类都会非常糟糕地工作。当您查看为Console.WriteLine()生成的机器代码时,您可以看到它正常工作,当您传递一个简单的字符串时,它经常被内联。要自己查看,需要切换到Release版本并更改调试器选项。工具+选项,调试,常规,取消勾选“在模块加载时抑制JIT优化”。
否则没有充分的理由考虑MethodImpOptions.NoInlining malign,这就是它首先存在的原因。事实上它在.NET框架中故意用于许多调用内部帮助器方法的小型公共方法。它使异常堆栈跟踪更容易诊断。
尽管Hans Passant的回答是,here在2004年首先提出了几个提示,并进一步提供了一些更新的信息。它们可能会发生变化,但如果您想使方法符合内联条件,它们确实可以让您了解要查找的内容:
JIT不会内联:
- 用MethodImplOptions.NoInlining标记的方法
- 大于32字节的IL的方法
- 虚拟方法
- 将大值类型作为参数的方法
- MarshalByRef类的方法
- 具有复杂流程图的方法
- 满足其他更奇特标准的方法
特别是,有MethodImplOptions.AggressiveInlining,它应该提升32个字节的限制(或者不管你现在和你的平台发生什么)。
.Net 3.5添加了启发式方法,帮助它确定To Inline or not to Inline,这可能是一件好事,尽管它让开发人员更难以预测抖动的决定:
文章的引用:
- 如果内联使代码变小,那么它所替换的调用就会很好。请注意,我们讨论的是NATIVE代码大小,而不是IL代码大小(可能完全不同)。
- 特定呼叫站点的执行越多,它就越受益于inlning。因此,循环中的代码应该比不在循环中的代码更内联。
- 如果内联暴露了重要的优化,那么内联是更理想的。特别是具有值类型参数的方法比正常情况受益更多,因为这样的优化因此具有内联这些方法的偏见是好的。
因此,给定内联候选者,X86 JIT编译器使用的启发式。
- 如果未内联方法,则估计调用站点的大小。
- 估计呼叫站点的大小(如果它是内联的(这是基于IL的估计,我们使用简单的状态机(马尔可夫模型),使用大量实际数据创建以形成此估计逻辑)
- 计算乘数。默认情况下为1
- 如果代码处于循环中,则增加乘数(当前启发式在循环中将其颠覆为5)
- 如果看起来结构优化将会起作用,请增加乘数。
- 如果InlineSize <= NonInlineSize * Multiplier执行内联。
虽然Hans' answer是正确的,但有一个遗漏,不一定是关于何时一个方法有资格进行内联,但是当一个方法不符合时。
Abstract and virtual methods are not eligible for inlining in the CLR。
重要的是要注意,因为它可以简化方法的内联条件。
有关于此线程http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html上的MethodBase.GetCurrentMethod的内联的更多信息
大量释义,它声明RefCrawlMark不会停止内联的调用方法。但是,RequireSecObject确实具有停止内联调用者的副作用。
此外,Assembly.GetCallingAssembly和Assembly.GetExecutingAssembly方法没有此属性。
2003年MSDN上发布了一篇名为Writing High-Performance Managed Apps的文章,其中非常清楚地概述了几个标准:
Sacha Goldshtein在2012年关于aggressive inlining in the CLR的博客文章提出了许多相同的建议。