编辑:JIT编译器的工作方式与您想象的不同,这可能是您没有完全理解我上面试图传达的原因。我引用了您下面的评论:
那是另一回事,但据我所知,只有在方法被调用足够多次时才会检查它们是否值得内联。更不用说检查本身就会影响性能了。(现在让性能损失的大小无关紧要。)
首先,大多数方法都会被检查以确定它们是否可以内联。其次,请记住,方法只会被JIT一次,而且正是在那一次中,JIT编译器将确定其中任何被调用的方法是否会被内联。这可以在程序执行任何代码之前发生。什么使被调用的方法成为内联的好候选者?
x86 JIT编译器(x64和ia64不一定使用相同的优化技术)检查一些内容以确定一个方法是否是内联的好候选者,绝对不只是它被调用的次数。
这篇文章列出了一些内容,例如内联是否会使代码更小,调用点是否将在循环中执行等。每个方法都是根据自己进行优化的,因此该方法可能会被内联到一个调用方法中,但在另一个调用方法中却不会,例如在循环中的情况。这些优化启发式算法仅适用于JIT,C#编译器并不知道:它正在生成IL而非本机代码。它们之间有一个
巨大的区别;本机代码与IL代码大小可能会有很大的不同。
总之,C#编译器不会为了性能原因内联属性。
JIT编译器会内联大部分简单属性,包括自动属性。您可以在
这篇有趣的博客文章中了解更多JIT如何决定内联方法调用的信息。
C#编译器根本不会内联任何方法。我认为这是因为CLR的设计方式。每个程序集都被设计成可在不同机器之间移植。很多时候,您可以改变.NET程序集的内部行为而无需重新编译所有代码,只需进行替换即可(至少在类型未更改时如此)。如果将代码内联,它会破坏这种优秀的设计,您将失去这种光彩。
让我们先谈论C++中的内联。(完全透明,我已经有一段时间没有全职使用C++了,所以我的解释可能含糊、生疏或者完全错误!我指望我的SOers来纠正和指责我)
C ++
内联关键字 就像告诉编译器:“嘿,我想要内联这个函数,因为我认为它会提高性能。”不幸的是,它只是告诉编译器您更喜欢它内联; 它并没有告诉它必须这样做。
也许在早期,当编译器不如现在优化时,编译器往往会将该函数编译成内联。然而,随着时间的推移和编译器变得更加智能,编译器编写人员发现,在大多数情况下,他们比开发人员更擅长确定何时应将函数内联。对于那些不适用的情况,开发人员可以使用seriouslybro_inlineme
关键字(在VC++中正式称为__forceinline
)。
现在,编译器的作者为什么要这样做呢?嗯,内联函数并不总是意味着性能提升。虽然它确实可以,但如果使用不当,它也可能毁掉你程序的性能。例如,我们都知道内联代码的一个副作用是增加代码大小,或者说“肥大代码综合症”(免责声明:这不是一个真正的术语)。为什么“肥大代码综合症”是个问题呢?如果你看一下我上面链接的文章,它解释了,除其他外,内存很慢,而你的代码越大,它就越不可能适应最快的CPU缓存(L1)。最终它只能适应内存,那么内联就没有起到任何作用。然而,编译器知道这些情况何时会发生,并尽力防止它。
结合你的问题,让我们这样来看待它:C#编译器就像为JIT编译器编写代码的开发人员:JIT更加聪明(但不是天才)。它通常知道内联何时有益或有害于执行速度。"高级开发人员" C#编译器不知道内联方法调用如何有利于代码的运行时执行,因此它不会这样做。我想这实际上意味着C#编译器很聪明,因为它将优化的工作留给比它更好的人,也就是JIT编译器。