.NET代码经过JIT编译后执行时,与本地代码是否相同?

3
.NET语言都编译成中间语言(MSIL)。据我所知,在执行期间(有时在其他阶段,我不是很了解——NGEN),代码正在被JITted(从MSIL编译为实际的机器码)。我想知道,在JITting代码后,代码是否存在性能“惩罚”,因为代码在CLR上执行,还是代码的行为与任何其他本地代码“相同”?
3个回答

5

有一些性能差异:

  1. 托管对象的自由存储空间实现为栈,而不是堆(除了大对象堆),开销比大多数本地分配器使用的堆要低。但之后你需要付出垃圾回收和压缩的代价。

  2. JIT 可以内联一些调用,而 AOT 编译器必须保留虚拟调用(即调用其他程序集)。但 AOT 编译器可以花更多时间寻找优化机会。

  3. 理论上,JIT 可以使用特定 CPU 上运行代码的高级指令(例如 AVX)。但是,我们仍在等待一个真正充分利用它们的 JIT。

  4. AOT 编译器可以使用分析数据来控制代码内存的布局。JIT 编译器几乎总是按照编译的顺序将函数发射到内存中。


这里有一个(过时的)CPU特定优化列表,CLR在此进行了说明:http://blogs.msdn.com/b/davidnotario/archive/2005/08/15/451845.aspx - Sean U
我猜你在第一段谈论的是bump-pointer分配。如果是这样,那么这与JIT无关(除了它可能会内联快速路径),解释器和AOT编译的、带有垃圾回收功能的语言实现(如GHC)都会做同样的事情。 - user395760
@delnan:这个问题涉及“任何其他本地代码”。大多数本地代码都不使用bump-pointer。因此,这是.NET和本地代码之间的一个有趣差异,超越了JITting过程。“垃圾收集”基本上是“托管”的默认含义;该问题并不是将CLR与其他托管框架进行比较,而是与“本地”代码进行比较。 - Ben Voigt
AOT编译与垃圾回收完全独立。当考虑CLR性能与C/C++性能时,撞针分配是一个有趣的点,但混淆这两个问题并没有帮助。我们已经有足够多的人对语言实现感到极度困惑了。 - user395760
1
@delnan:我提出了多个独立的观点。这就是段落分隔符的含义。关于免费存储器的段落没有提到JIT vs AOT。如果我给它们编号并在它们之间加上水平线,是否有帮助? - Ben Voigt

1
JIT编译的代码主要性能损失在于第一次运行时编译代码所需的时间。虽然这通常只表现为稍微长一点的启动时间(也许几乎察觉不到),但如果您在像CGI这样的场景中使用它,那么它可能会受到真正的打击。当每个请求都需要生成一个新进程时,情况就是如此。虽然.NET编写的CGI脚本并不是常见的用例,但这是我想到的第一个例子,所以我将继续使用它。
NGen可以通过跳过JIT步骤来提高启动时间。该好处在于短时间内频繁运行的程序,例如CGI脚本,受益最大。(或者,现在我想起来了,设置为自动启动的Windows服务可能更好地说明问题。)对于很少运行的程序,可执行文件不太可能被缓存在内存中,因此每次都需要从磁盘加载。从磁盘读取所需的时间可能会支配启动时间,并压倒NGen的好处。对于长时间运行的程序,启动时间可能并不是一个重要的性能特征。

0

通常本地编写的代码将更具性能,因为您可以使用特定架构可用的优化,这些优化可能需要在设计时了解,例如SSE。还有其他要考虑的事情,例如垃圾回收与手动内存管理对性能的影响。

就NGEN而言,我认为JITTED代码和NGEN所做的之间不会有太大的性能差异,除了JITTED代码是在运行时生成的(当实际编译发生时会产生性能损失)。


我的问题涉及“jitting之后会发生什么”,CLR是否会增加任何开销,还是就像运行本地代码一样? - lysergic-acid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接