在参考某些.Net代码的时候,我们有时会看到某些方法,甚至属性的 get 或 set 代码片断上会附加 [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] 属性,那么这个属性是做什么用的呢?MSDN上对此语焉不详。
对此最权威的一次引用来自于Writing Faster Managed Code: Know What Things Cost,上面提到应用该属性以避免编译器在优化编译时因为该方法过于简短而作为内联(inline)函数而合并。Rico Mariani在Performance Quiz #4 -- Of Sets and Sundry -- Solution的评论里给出了进一步的解释,即System.Runtime.CompilerServices.MethodImplOptions.NoInlining 是作为性能基准标记而存在。
Rockford Lhotka给出了最细致的解释:
Inlining(内联)是一项编译器优化功能,用于避免函数的冗繁调用,换句话说,如果被调用的函数代码段足够简短那么就把代码直接合并到发起调用的函数内。这是一个非常简单但很有用的性能优化措施,也是编译器最常应用的一个功能。
本质上说,你有某段代码A,调用函数B,此调用在底层实现时会执行以下步骤,把A和B压入堆栈,线程控制转移给B,处理堆栈操作到调用完成(胡健注:线程控制权再转移回A)。这一系列步骤不是非常冗繁,但还是稍嫌冗繁了些(尤其是在循环结构体内执行时)。Inlining在此的用处就在于,将函数B的代码直接合并到代码段A中,这样代码段A就不用真正地调用函数B了,因为代码段A中已经包含函数B的代码了。
一切看来都是那么美好!但是,...使用堆栈跟踪来查找属性名称,若代码被inlined的话,响应对代码段B的调用的将会是代码段A。...这将导致你的程序被不正确地执行(不是程序崩溃-只是运行得不同于期望)。
总的说来,对 System.Runtime.CompilerServices.MethodImplOptions.NoInlining 的应用应被视为一种最佳实践而执行。