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

问题描述 投票:37回答:5

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

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

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

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

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

什么时候CLR允许内联?

.net clr jit inlining
5个回答
23
投票

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

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

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


6
投票

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

JIT不会内联:

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

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

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

文章的引用:

  1. 如果内联使代码变小,那么它所替换的调用就会很好。请注意,我们讨论的是NATIVE代码大小,而不是IL代码大小(可能完全不同)。
  2. 特定呼叫站点的执行越多,它就越受益于inlning。因此,循环中的代码应该比不在循环中的代码更内联。
  3. 如果内联暴露了重要的优化,那么内联是更理想的。特别是具有值类型参数的方法比正常情况受益更多,因为这样的优化因此具有内联这些方法的偏见是好的。

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

  1. 如果未内联方法,则估计调用站点的大小。
  2. 估计呼叫站点的大小(如果它是内联的(这是基于IL的估计,我们使用简单的状态机(马尔可夫模型),使用大量实际数据创建以形成此估计逻辑)
  3. 计算乘数。默认情况下为1
  4. 如果代码处于循环中,则增加乘数(当前启发式在循环中将其颠覆为5)
  5. 如果看起来结构优化将会起作用,请增加乘数。
  6. 如果InlineSize <= NonInlineSize * Multiplier执行内联。

3
投票

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

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

重要的是要注意,因为它可以简化方法的内联条件。


1
投票

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

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

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


0
投票

2003年MSDN上发布了一篇名为Writing High-Performance Managed Apps的文章,其中非常清楚地概述了几个标准:

  • 不会内联大于32字节IL的方法。
  • 虚函数未内联。
  • 具有复杂流量控制的方法不会被列入内联。复杂流量控制是除if / then / else之外的任何流量控制;在这种情况下,切换或同时。
  • 虽然抛出异常的方法仍然是内联的候选方法,但不会内联包含异常处理块的方法。
  • 如果方法的任何形式参数都是结构体,则不会内联该方法。

Sacha Goldshtein在2012年关于aggressive inlining in the CLR的博客文章提出了许多相同的建议。

© www.soinside.com 2019 - 2024. All rights reserved.