.net 什么时候一个方法可以被编译器内联?

f0brbegy  于 2023-11-20  发布在  .NET
关注(0)|答案(5)|浏览(129)

我在应用程序中观察到了很多“堆栈内省”的代码,它们通常隐式地依赖于它们的包含方法 not 被内联来保证它们的正确性。

  • 第一个月
  • Assembly.GetCallingAssembly
  • Assembly.GetExecutingAssembly

现在,我发现围绕这些方法的信息非常令人困惑。我听说运行时不会内联调用GetCurrentMethod的方法,但我找不到任何相关文档。我在StackOverflow上看到过几次帖子,比如这篇文章,表明Python不会内联交叉汇编调用,但GetCallingAssemblydocumentation强烈表明相反。
还有一个备受诟病的[MethodImpl(MethodImplOptions.NoInlining)],但我不确定Linux认为这是一个“请求”还是一个“命令”。
请注意,我是从契约的Angular 询问内联 * 资格 *,不是关于JITter的当前实现何时因为实现困难而拒绝考虑方法,或者JITter在评估权衡后最终 * 选择 * 内联一个合格的方法。我读过thisthis,但他们似乎更关注最后两点(有顺便提到的MethodImpOptions.NoInlining和“外来IL指令”,但这些似乎被呈现为抽象而不是 * 义务 *)。
什么时候允许内联?

9bfwbjaz

9bfwbjaz1#

这是一个jitter实现细节,x86和x64 jitter有微妙的不同规则。这是在jitter团队成员的博客文章中随意记录的,但团队肯定保留更改规则的权利。看起来你已经找到了。
从其他程序集的内联方法是肯定支持的,如果不是这样的话,很多.NET类将工作得很糟糕。当你查看为Console.WriteLine()生成的机器代码时,你可以看到它在工作,当你传递一个简单的字符串时,它经常被内联。要亲自看看这一点,你需要切换到发布版本并更改调试器选项。工具>选项>调试器>常规,取消勾选“在模块加载时抑制JIT优化”。
否则就没有理由考虑使用MethodImpOptions了。NoInlining被恶意中伤,这也是它存在的原因。事实上,它在.NET框架中被有意地用在许多调用内部帮助器方法的小公共方法上。它使异常堆栈跟踪更容易诊断。

0aydgbwb

0aydgbwb2#

尽管Hans Passant给出了答案,here首先给出了一些2004年的提示,然后是一些最新的信息。它们可能会发生变化,但如果你想让一个方法适合内联,它们确实给了你一个给予关于寻找什么的想法:
JIT不会内联:

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

特别是,有MethodImplOptions.AggressiveInlining,它应该解除32字节的限制(或者现在和你的平台上发生的任何事情)。
.Net 3.5增加了一些算法,可以帮助它确定To Inline or not to Inline是否是一件好事,尽管这使得开发人员更难预测jitter的决定:
文章中的一句话:
1.如果内联使代码比它所替换的调用更小,那么它总是好的。请注意,我们谈论的是NATIVE代码大小,而不是IL代码大小(可能会有很大的不同)。
1.一个特定的调用点被执行的次数越多,它从内联中受益的就越多。因此,处于循环中的代码比不在循环中的代码更值得内联。
1.如果内联暴露了重要的优化,那么内联是更可取的。特别是具有值类型参数的方法比普通方法受益更多,因为这样的优化,因此倾向于内联这些方法是好的。
因此,X86 JIT编译器使用的启发式方法是,给定一个内联候选。
1.如果方法未内联,则估计调用站点的大小。
1.估计调用站点的大小(如果它是内联的)(这是基于IL的估计,我们使用一个简单的状态机(马尔可夫模型),使用大量真实的数据创建以形成此估计器逻辑)
1.计算乘数。默认情况下为1
1.如果代码处于循环中,则增加乘数(当前启发式算法在循环中将其提升为5)
1.如果看起来结构优化会起作用,则增加乘数。
1.如果InlineSize <= NonInlineSize * Multiplier,则执行内联。

disbfnqx

disbfnqx3#

虽然Hans的回答是正确的,但有一个遗漏,不一定是关于一个方法何时适合内联,而是关于一个方法何时不适合内联。
抽象方法和虚方法不适合在对象中内联。
重要的是要注意,因为它减少了方法可以内联的条件。

5fjcxozz

5fjcxozz4#

这里有更多关于线程http://prdlxvm0001.codify.net/pipermail/ozdotnet/2011-March/009085.html上内联MethodBase.GetCurrentMethod的信息
它声明了RefCrawlMark不会停止调用方法的内联。然而,RefreSecObject确实有停止调用者内联的副作用。
此外,Assessment.GetCallingAssembly和Assessment.GetExecutingAssembly方法没有此属性。

2o7dmzc5

2o7dmzc55#

2003年,在MSDN上发表了一篇名为“编写高性能托管应用程序”的文章,其中非常清楚地概述了几个标准:

  • 超过32字节的IL方法将不会内联。
  • 虚函数不是内联的。
  • 具有复杂流控制的方法将不会内联。复杂流控制是除if/then/else之外的任何流控制;在本例中,是switch或while。
  • 包含异常处理块的方法不是内联的,尽管抛出异常的方法仍然是内联的候选方法。
  • 如果方法的任何形式参数是结构,则该方法将不会内联。

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

相关问题