【问题标题】:when is java faster than c++ (or when is JIT faster then precompiled)? [duplicate]java什么时候比c++快(或者JIT什么时候比预编译快)? [复制]
【发布时间】:2011-05-29 20:12:56
【问题描述】:

可能重复:
JIT compiler vs offline compilers

我听说在某些情况下,由于 JIT 优化,Java 程序或更确切地说是部分 Java 程序能够比 C++ 中的“相同”代码(或其他预编译代码)更快地执行。这是因为编译器能够确定一些变量的范围,避免一些条件并在运行时提取类似的技巧。

你能否举一个(或更好的——一些)例子,这适用于什么地方?并且也许概述了编译器能够优化字节码超出预编译代码可能的确切条件?

注意:这个问题不是关于比较 Java 和 C++。它关于 JIT 编译的可能性。请不要着火。我也不知道有任何重复。如果有请指出来。

【问题讨论】:

  • 这个实际上是重复的。带来不便敬请谅解。请合并

标签: java performance optimization compiler-construction jit


【解决方案1】:

维基百科:http://en.wikipedia.org/wiki/Just-in-time_compilation#Overview

此外,在某些情况下,它可以提供比静态编译更好的性能,因为许多优化只在运行时才可行

  1. 编译可以针对目标 CPU 和应用程序运行所在的操作系统模型进行优化。例如,当 JIT 检测到 CPU 支持它们时,它可以选择 SSE2 CPU 指令。要使用静态编译器获得这一级别的优化特异性,必须为每个预期的平台/架构编译一个二进制文件,或者在单个二进制文件中包含代码部分的多个版本。

  2. 系统能够收集统计数据关于程序在其所在环境中的实际运行情况,并且它可以重新排列和重新编译以获得最佳性能。但是,一些静态编译器也可以将配置文件信息作为输入。

  3. 系统可以进行全局代码优化(例如库函数的内联),而不会失去动态链接的优势,也不会产生静态编译器和链接器固有的开销。具体来说,在进行全局内联替换时,静态编译过程可能需要运行时检查,并确保在对象的实际类覆盖内联方法时会发生虚拟调用,并且边界条件检查数组访问可能需要在循环中处理。在许多情况下使用即时编译可以将这种处理移出循环,通常会大大提高速度

  4. 虽然这对于静态编译的垃圾收集语言是可能的,但字节码系统可以更轻松地重新排列执行的代码以更好地利用缓存

【讨论】:

  • 很好的信息,但仔细阅读会发现预编译实际上可以并且确实可以进行许多“仅 JIT”优化。
  • @BenVoigt 说得好。剩下的主要论点是 JIT 可以访问特定于正在运行的进程的信息,而不是预先创建的配置文件或类似的信息。因此,它可以更频繁地执行大胆的优化,并获得更大的成功机会。
【解决方案2】:

一些例子:

  • JIT 编译器可以生成非常特定于 CPU 的机器代码,例如使用最新的 SSE 扩展,不会在需要运行各种 CPU 的预编译代码中使用。
  • JIT 知道何时虚拟方法(Java 中的默认方法)没有在任何地方被覆盖,因此可以内联(尽管这需要能够在加载确实覆盖该方法的新类时取消内联它;当前Java JIT 编译器实际上就是这样做的)。
  • 与此相关,escape analysis 允许针对特定情况进行多种优化。

【讨论】:

  • 第一点同样有效,因为许多 Java 库是在新的 CPU 架构可用之前编写的。这些旧库仍然使用最新的 CPU 改进。要使用 C++ 中的最新架构,您必须能够从源代码编译,这对于第三方库来说很多是不可能的/不实用的。尤其是如果开发人员不是最终用户。例如您有一个应用程序必须部署到许多不同类型的 PC 上,发布所有可能的平台可能是一场噩梦,因此通常会选择最小的公分母。
  • 我相信 JIT 可以执行多态内联。即它知道最多两个可能的“虚拟”方法,这些方法通常被调用,并且可以内联,如果对象不是这些类之一,则有一个后备。这意味着即使是具有多种可能实现的虚拟方法也可以根据运行时行为进行内联。
  • C++ 配置文件引导优化器使用这些相同的技巧。
【解决方案3】:

Java 的内存管理比 C++ 快得多。见Java theory and practice: Urban performance legends, revisited

【讨论】:

  • 一个好的链接有时和一个答案一样好。很有见地。谢谢
  • 很好的稻草人论点。 C++ 不要求您使用malloc,例如参见wireshark 的池分配器。
  • 那么该参数不适用于带有 new 的 C++ 分配吗?是不是因为 HeapAlloc 等分配的堆用于 new 分配?
【解决方案4】:

在实践中,您可能会发现在这些情况下,您编写的幼稚 Java 代码的性能优于幼稚编写的 C++ 代码(所有这些都是我亲自观察到的):

  • 大量的小内存分配/释放。主要的 JVM 都有非常高效的内存子系统,垃圾收集比要求显式释放更有效(如果它真的想的话,它可以移动内存地址等)。

  • 通过方法调用的深层层次结构进行高效访问。 JVM 非常擅长删除任何不必要的东西,根据我的经验,通常比大多数 C++ 编译器(包括 gcc 和 icc)要好。部分原因是它可以在运行时进行动态分析(即,它可以过度优化,只有在检测到问题时才去优化)。

  • 将功能封装到短寿命的小对象中。

在每种情况下,如果你付出努力,C++ 可以做得更好(在空闲列表和块分配/解除分配的内存之间,C++ 几乎在所有特定情况下都可以击败 JVM 内存系统;通过额外的代码、模板和聪明的宏,您可以非常有效地折叠调用堆栈;并且您可以在 C++ 中拥有小型的部分初始化堆栈分配对象,其性能优于 JVM 的短期对象模型)。但你可能不想付出努力。

【讨论】:

  • 谢谢。这是我希望的详细程度。
猜你喜欢
  • 1970-01-01
  • 2010-10-16
  • 2021-03-10
  • 1970-01-01
  • 2021-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多