16

Java 在 JIT 之前最初很慢,但今天的性能非常接近 C++。我想知道是否有人在两种语言之间进行了可衡量的性能比较? 与 C++ 相比,Java 的不足之处在哪里? Java 为开发人员提供了许多生产力提升,因此他们可以更快地编写应用程序,因为垃圾大学、缺乏指针等。例如,Firefox、Webkit 和 Open Office 等应用程序如果以 100 编写,则可以更快、更可靠地开发% Java,可能是 2 倍,但出于性能原因,开发人员仍然选择 C/C++。有人可以证明 Java 不能像我提到的应用程序那样在 C++ 中表现得一样好。

让我补充一点,很多应用程序工作仍在 C++ 中完成是有原因的。这不仅仅是一个主观问题。具有更高抽象级别的语言经常会付出性能损失。如果不存在这种惩罚,我们都将使用高级语言进行编程。与 C++ 相比,Java 的优势在哪里?请明确点。

4

13 回答 13

34

语言没有速度。Java 或 C++ 语言规范都没有指定“并且程序必须编译为如此高效”。

每种语言都指定了程序必须做的事情的列表,或者至少看起来要做的事情,这在某些情况下为程序的效率设定了上限,但通常,聪明的编译器可以在单个程序中忽略这些规则,因为重要的是程序的行为就像遵循了规范一样。函数可以内联,堆数据可以移动到堆栈等等。

程序的性能取决于三件事:编译器、底层平台/硬件和程序代码本身。

不是“语言”。你得到的最接近的是编译器。

两种语言都比另一种更快是有充分理由的。C++ 做出的承诺可能会减慢程序执行速度,但 Java 是 JIT 的,这意味着它可能会利用运行时信息来优化代码,而 C++ 不能轻易做到这一点......规范是否说 C++不能被 jit'ed。就像我相信也有 Java 编译器生成本机代码而不是 JVM 字节码一样。

只有当您有一台正在运行的特定计算机、每种语言的特定编译器以及每种语言的程序的特定实现时,您的问题才有意义,在这种情况下,您可以同时运行两者以查看哪个是最快的。

垃圾收集是另一个很好的例子。当然,垃圾收集意味着一些开销,但它也启用了一些重要的捷径。在 Java 或 .NET 等托管语言中,堆分配非常便宜,因为它是托管和垃圾收集的。在 C++ 中,它是....未指定,当然,但在实践中,通常非常慢,因为操作系统必须遍历堆才能在或多或少碎片化的内存空间中找到空闲的内存块。哪个最快?取决于操作系统。取决于编译器。取决于源代码。

源代码也有很大的不同。如果你将一个 Java 程序天真地移植到 C++ 中,它的表现会像垃圾一样。C++ 不能很好地处理虚函数,通常有更好的替代方案可供您使用。堆分配在 C++ 中可能非常慢,因此再次天真地重新实现 Java 程序将是极其低效的。当走相反的路时也是如此。如果直接移植到 Java,许多 C++ 习惯用法会不必要地变慢。因此,即使您选择了一个平台和一个编译器,您如何比较程序的性能?甚至要让它编译器,你必须编写它的两个实现,然后它就不再是同一个程序。

但是,我认为公平地说,在大多数现代硬件上,使用现代 Java 编译器和现代 C++ 编译器,大多数程序都可以非常高效地实现,而且速度肯定足够快。但前提是您了解您正在使用的语言并遵守其规则。如果您尝试用 C++ 编写 Java 代码,那么 Java 会神奇地变得更加高效,反之亦然。

我想对您的问题最简洁的回答是“不。没有人可以量化 C++ 和 Java 之间的性能差异”;)

于 2008-11-24T09:24:07.653 回答
16

对于许多单独的代码构造,JIT 编译器可以更快,因为它们可以利用代码的运行时分析。

例如,VonC 在他对这个问题的回答中提到了所有对象的堆分配。这实际上是不正确的:如果 JIT 可以通过转义分析证明对对象的引用不会超过堆栈帧,则它可以在堆栈上分配对象。通过这种方式,编译器可以获得堆栈分配的性能优势,而程序员可以放心假定的 GC 堆分配的安全性。

类似地,Uri 提到了虚函数(在大多数非 C++ 语言中称为虚方法)。这是另一种情况,JIT 编译器具有提前 (AOT) 编译器几乎永远无法获得的优势:JIT 可以插入内联的廉价类型检查(取消引用的字比较),并且实际上内联虚拟方法调用,如果该特定调用站点恰好是单态的(即实际类型在实践中总是相同的)。事实证明,在实践中,多达95% 的虚拟方法调用都是单态的,所以这可能是一个很大的胜利——而且对于 AOT 编译器来说,这是一个很难利用的胜利,因为运行时代码加载可能会改变运行时特性动态的。

于 2008-11-24T05:28:58.063 回答
12

为了完成 Pax 和 Uri 的回答,这里有一些最近的基准:

如前所述,这是两种截然不同的语言,有些人相信Java 将永远比 C++ 慢,因为:

  • 所有对象的堆分配(即使是像迭代器这样的小对象)
  • 大量动态铸件
  • 内存使用量增加

[幽默]

“Java 是高性能。高性能意味着足够。足够意味着慢。” 兔子先生

正如dribeas在评论中提到的,堆分配不是一个好的论据。
这部《都市表演传奇,重温》提到:

“垃圾收集永远不会像直接内存管理那样高效。” 而且,在某种程度上,这些陈述是正确的——动态内存管理没有那么快——它通常要快得多
malloc/free 方法一次处理一个内存块,而垃圾收集方法倾向于处理大批量的内存管理,从而产生更多优化机会(以损失一些可预测性为代价)。

于 2008-11-24T05:10:38.290 回答
9

另一个板凳:枪战

于 2008-11-24T07:39:40.640 回答
7

在许多方面,这就像将苹果与橙子进行比较。

C++ 建立在这样一个概念之上,即您无需为不使用的任何东西付费。如果您自己管理内存,如果您不使用虚拟功能等。

Java 没有给你这种自由。它为您提供了您甚至可能不想要的功能。对于您可能想要自己分配内存的所有内容,您必须对所有内容使用堆对象,因此您将受到垃圾收集的影响。

一旦您开始谈论 GUI,就很难进行比较,因为不同的 UI 框架和工具包具有不同的性能问题。例如,Swing/AWT 通常会比直接为本机操作系统编写的内容要慢。在 C++ 中,您很少会找到真正可移植的工具包等。

我认为当开发人员开始使用 openoffice 时,Java 速度要慢得多,UI 工具包又慢又丑。Eclipse 之类的工具证明即使在 Java 中您也可以构建相对不错的 UI,尽管不可否认,SWT 是一个在本机级别上做很多事情的工具包。

于 2008-11-24T05:01:16.030 回答
7

许多人忘记的是,JIT 技术可以应用于任何类型的二进制文件,甚至是由 C++ 编译器生成的二进制文件。如果您使用诸如 HP 的 Dynamo 之类的东西(一个运行可执行文件的仿真器比它运行和仿真的本机芯片更快),Java 的 JIT 编译的大部分好处也适用于 C++。运行时分析并不是 Java 的真正性能优势,而是 JIT 编译的一般优势。

于 2008-11-24T13:26:00.287 回答
6

对我来说,这个问题有点牵强附会(也许不是故意的)。它确实是一个错误的问题。

首先要问的问题是这些

  1. 是什么让我的程序运行缓慢?
  2. 对于我的新程序,关键性能设计考虑因素是什么?

这里有一些很好的“为什么”问题

  • 是否有太多不必要的 I/O?
  • 是否使用了太多内存?
  • 内存分配器是否被破坏(分配器太多,细粒度对象太多)
  • 我的程序是否在网络 I/O 上长时间阻塞
  • 把锁放在错误的地方

我怀疑您确实需要首先关注程序的性能方面(带有大写“P”)而不是性能(小“p”)方面如果你能达到语言障碍的地步,那么你在性能方面做得非常好。

对于新代码 - 提前计划性能和效率很重要。我总是建议像对待任何其他特性(它们是特性)一样对待性能和效率:就像 UI bling 或可靠性一样。 当然,这取决于很多事情——但是当它很重要时,你需要预先计划:

  • 选择适合数据集和预期扩展的数据结构和算法
  • 适当的基于多线程 UI 的应用程序(UI 线程、后台/处理线程)
  • 规划较长的网络 I/O 延迟
  • 计划设定目标并预先衡量绩效 - 定期运行回归测试
  • 测量内存使用 - 内存消耗很慢(让笑话开始:))
  • 有事件、回调或其他通知机制时不要轮询

我认为这是一个红鲱鱼的原因是很少有人可以在 C++ 和 Java 之间进行选择——它们是非常非常不同的语言,具有非常不同的运行时间。我怀疑更常见的是,您有其他约束以一种或另一种方式推动您-这些将是比语言表现更高的阶数。现有代码的可计算性、现有员工的技能和经验等。

环境也有所不同。例如,Java 几乎永远不会成为 widows 客户端(相对于 Web)应用程序的正确选择。相反,原生 C++ 几乎永远不会成为基于 Web 的应用程序的选择。(注意,我是一个 Windows 人——在 *nix 中情况可能非常不同)。

于 2008-11-24T05:22:42.447 回答
5

我不相信任何人可以证明 C++ 总是比 Java 快得多,因为你总是可以恢复到 JNI 以从 Java 获得本机速度。

例如,请参阅 SWT,它是 IBM(我认为)构建的图形工具,旨在取代 Swing 并提供本机性能和外观。

一方面,我更喜欢易于开发而不是速度,因为我认为最短的开发时间比原始应用程序速度更重要,特别是当我仍然可以达到那个速度时 - 我可以同时拥有 Java 的易于开发编译语言的速度。

于 2008-11-24T05:02:46.933 回答
5

需要考虑的几点:

  • 如果你得到一个更好的 C++ 编译器,你的代码不会变得更快。您需要先重新编译它。如果你有一个更好的 JVM,你所有的 Java 代码都会运行得更快

  • 如果您获得更好的 C++ 编译器/JVM,您将看到执行速度提高 10-20%,通常是在极端情况下。如果您找到更好的算法来实现您所需要的,您可以轻松获得 1,000%-10,000% 的性能提升,有时甚至更多。

所以今天,如果性能是一个问题,你应该看看这两个事实:

  • 该语言使用另一种算法替换一种算法变得多么容易?(又名“重构”)
  • 您可以在其中编写代码多快?

其他任何事情都只是 FUD。

于 2008-11-24T09:55:22.940 回答
5

我已经用 C 和 Java 实现了对性能敏感的应用程序(物理模拟、金融模型)。现实情况是,通过更改算法比通过调整实现来获得更大的性能提升——但实现也很重要。就目前而言,我的观点是 Java 比 C 慢(我对 C++ 中的数字没有太多经验),但是通过仔细调整可以获得很多东西,而且这种调整在 Java 中要容易得多,因为你不必处理分段错误、双重释放等。C++ 在这里占据了中间位置,因为现代 C++ 技术(智能指针、模板、STL 容器)提供了速度和相对安全的使用。

于 2009-06-22T21:03:25.347 回答
3

与 C++ 相比,Java 的不足之处在哪里?

很好的问题。与 C++ 相比,Java 和 JVM 有两个主要缺陷会削弱性能:

  • 基于类型擦除的泛型。

  • 缺少值类型。

前者意味着通用代码会导致装箱和拆箱,这会导致大量不必要的分配和额外的对缓存不友好的间接级别。

后者意味着程序员不可能拆箱任意数据结构,如复数(浮点数对)、哈希表条目(键值对)和顶点数据。

这两个问题结合在一起,使得在 Java 中实现高效的通用哈希表成为不可能。特别是,.NET 解决了这两个问题。例如,Java 的通用哈希表可能比 .NET 慢 17 倍Dictionary

此外,与 C++ 相比,JVM 的 FFI 非常慢。我听说简单地从 Java 调用外部 C 函数可能需要 1,000 个周期。

于 2010-07-01T23:37:13.690 回答
1

有些东西最好用 Java、C# 或任何托管编程语言构建。使用非托管编程语言(如 C 或 C++)构建其他东西总是会更好

前一类通常包括“应用程序”,而第二类通常包括“平台”。

用 Java 构建 FireFox 或 WebKit 不仅愚蠢,而且会使最终产品变得非常非常缓慢、糟糕,并为最终用户浪费大量资源。就此而言,Open Office 可能是 Java、C# 或SmallTalk的理想选择。但是用 Java(或 C#)构建 FireFox 或 WebKit 是愚蠢的,而且是失败的保证......

对于许多事情,C++ 和 C 会快几个数量级,此外它会使用一小部分内存。就是那样子。只要 Java 和 C# 是“托管”编程语言,这将永远不会改变。也许有一天 CPU 如此之快以至于“没关系”。但我对此表示怀疑,因为随着更多 CPU 的分配,人们往往会调整他们的要求……

如果您想构建浏览器,很抱歉您需要自学 C 或 C++ ;)

于 2008-11-24T08:03:20.687 回答
1

Java 应用程序具有 C++ 程序没有的初始化开销。当 JITed 时,它们不像 C++ 程序那样进行微优化。此外,运行时开销很小(GC + 间接调用开销)。总的来说,这些可量化的差异加起来并不多。但...

众所周知,当 Java 应用程序第一次启动时,它必须激活其磁通电容器才能将其环境带回 1995 年。这会在启动时引入一点延迟。但是一旦结束,JVM 的性能就与 1995 年在硬件上运行的可比 C++ 程序差不多。

于 2009-10-25T02:19:27.717 回答