为什么经过JIT编译的语言仍然比本地C/C++更慢、内存效率更低?

7
解释器需要做很多额外的工作,所以可以理解它们的速度会显著慢于本地机器代码。但是,诸如C#或Java之类的语言具有JIT编译器,据说可以编译成平台本地机器码。
然而,根据看起来足够可信的基准测试,在大多数情况下仍比C/C++慢2-4倍?当然,我是指与同样优化的C/C++代码相比。我非常清楚JIT编译的优化好处及其能够生成比C + C++差的优化代码更快的能力。
经过所有这些关于Java内存分配有多好的噪声后,为什么会有如此可怕的内存使用情况?在该特定基准测试套件中,平均使用的内存增加了2倍到50倍,大约是原来的30倍,这不是小事......
请注意,我不想开始一场战争,我只是询问定义这些性能和效率数字的技术细节。

为什么Java曾经被认为是慢的? - assylias
虽然JIT编译器和离线编译器都需要平衡编译时间和执行速度,但离线编译器可以更加注重执行速度的平衡。 - harold
3个回答

7

造成差异的一些原因:

  • JIT编译器通常编译速度快,跳过一些需要更长时间查找的优化。

  • VM经常强制执行安全机制,这会减慢执行速度。例如,在.NET中,除非保证处于正确的范围内,否则始终对数组访问进行边界检查。

  • 使用SSE(如果适用,则性能很棒)在C++中很容易实现,而在当前的VM中很难实现。

  • 与VM相比,C++更注重性能而不是其他方面。

  • VM通常在返回给操作系统之前保留未使用的内存,似乎“使用”更多内存。

  • 有些VM将值类型(如int/ulong)的对象作为对象处理,增加了对象内存开销。

  • 有些VM经常自动对齐数据结构,浪费内存(以获得性能提升)。

  • 有些VM将布尔值实现为int(4字节),表明它们对内存保护关注不够。


1
+1 针对速度部分的回答很好,能否分享一些关于内存方面的见解呢?我知道使用垃圾收集器和为每个对象创建对象会带来相当大的开销。每个对象至少有一个字的开销(GC元数据),更频繁地是两个字。 - user395760

0
但是像C#或Java这样的语言有JIT编译器,据说可以编译成平台本地机器代码。
解释器最终也必须将其翻译为机器代码。但是JIT编译器花费更少的精力来编译和优化,以获得更好的启动和执行时间。在用户的角度看来,浪费时间进行编译会使感知性能变差,因此只有在像AoT编译器中一样只进行一次时才可能实现。
它们还必须监视编译结果,以重新编译和优化热点,或取消使用很少的路径。然后他们不得不偶尔触发GC。这些比普通编译二进制文件需要更多的时间。此外,JIT编译器和JIT编译程序的大内存使用可能意味着更低效的缓存使用,从而也降低了性能。
有关内存使用的更多信息,请参考此处。
Java的内存使用比C++更加重,因为:
- Java中每个对象都有8字节的开销,每个数组都有12字节的开销(32位;在64位Java中是两倍)。如果对象的大小不是8字节的倍数,则会将其向上舍入到下一个8字节的倍数。这意味着包含单个字节字段的对象占用16字节,并且需要4字节的引用。请注意,C++也为声明虚函数的每个对象分配指针(通常为4或8字节)。 - Java库的某些部分必须在程序执行之前加载(至少是程序“底层”使用的类)。这导致小型应用程序有显着的内存开销[citation needed]。 - Java二进制文件和本机重新编译通常都在内存中。 - 虚拟机本身消耗了大量内存。 - 在Java中,组合对象(使用B和C实例的A类)是使用对已分配的B和C实例的引用创建的。在C++中,当B和/或C实例存在于A中时,可以避免这些类型引用的内存和性能成本。 - 缺乏地址算术使得创建内存高效的容器,例如紧密间隔的结构和XOR链接列表,变得不可能。
在大多数情况下,由于Java的虚拟机、类加载和自动内存调整的大量开销,C++应用程序将比等效的Java应用程序消耗更少的内存。对于那些内存是选择语言和运行时环境的关键因素的应用程序,需要进行成本/收益分析。
还应该记住,使用垃圾回收器的程序可能需要比使用显式内存管理的程序多达五倍的内存才能达到相同的性能。

-1

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