我在应用程序中观察到了很多“堆栈内省”代码,这些代码通常隐含地依赖于它们包含的方法而不是被内联的正确性。这些方法通常涉及调用:
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 何时允许内联?
答案 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,这可能是一件好事,尽管这会让开发人员更难预测抖动的决定:
文章引用:
如果内联使代码变小,那么它取代的调用就会很好。请注意,我们讨论的是NATIVE代码大小,而不是 IL代码大小(可能完全不同)。
执行特定呼叫站点的次数越多,它就越有利于inlning。因此,循环中的代码应该更多地内联 而不是循环中的代码。
- 醇>
如果内联公开了重要的优化,那么内联是更理想的。特别是具有值类型参数的方法 由于这样的优化,因此比正常受益更多 偏向内联这些方法是好的。
因此,给定内联的X86 JIT编译器使用的启发式算法 候选者。
如果方法未内联,请估算调用网站的大小。
估计呼叫站点的大小(如果它是内联的)(这是基于IL的估计,我们使用简单的状态机(Markov) 模型),使用大量实际数据创建,形成此估算逻辑)
计算乘数。默认情况下为1
如果代码处于循环中,则增加乘数(当前启发式循环将其变为5)
如果看起来结构优化会起作用,请增加乘数。
- 醇>
如果InlineSize< = NonInlineSize * Multiplier执行内联。
答案 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的文章,其中很清楚地涵盖了几个标准:
Sacha Goldshtein在2012年的aggressive inlining in the CLR上发表的博客文章也有很多相同的建议。