【问题标题】:Java Runtime Performance Vs Native C / C++ Code?Java 运行时性能与本机 C/C++ 代码?
【发布时间】:2010-12-31 09:19:33
【问题描述】:

与使用 C++ 或 C 相比,使用 Java 进行编程变得越来越舒服。我希望了解使用 JVM 解释器所带来的性能损失,而不是在本地执行相同的“项目”。我意识到这里存在某种程度的主观性。方案的质量在很大程度上取决于良好的实施。一般来说,我对以下几个方面感兴趣:

  • 使用解释器时必须有一些开销基线。是否有一些一般的经验法则要记住? 10% 15%? (我凭空得出了这些数字)我偶尔阅读过一篇博客,其中指出 Java 代码几乎与本机代码一样快,但我认为这可能是有偏见的。

  • JVM 垃圾收集器是否会显着增加运行时性能的开销?我知道 Cocoa 应用程序已经开始使用垃圾收集模型,我同意它使编程变得更简单,但代价是什么?

  • 从 Java 进行系统调用的开销是多少?例如创建一个 Socket 对象,而不是 C 套接字 API。

  • 最后,我记得在某处读到 JVM 实现是单线程的。如果这是真的(我对此表示怀疑),这是否意味着 Java 线程真的不是真正的线程?一般来说,java 线程是否对应于底层内核提供的线程? Java 应用程序是否以与本机应用程序从多核/多 CPU 相同的方式受益?

非常感谢了解 JVM 和 Java 程序性能的复杂性的开发人员提供的任何建议。谢谢。

【问题讨论】:

  • 为了澄清我帖子中的困惑,我并不是要比较 Java 语言与 C++ 语言的速度。我意识到比较语言的速度是没有意义的。我的意思是比较在 JVM 中运行的程序与在本地运行的程序的速度。
  • 没有任何“真正的”混淆。

标签: java c++ c jvm


【解决方案1】:

Java 不是一种解释性语言,并且没有几个版本。 Java 字节码是即时 JIT 的。 (从技术上讲,它仍然会解释一些代码,但任何与性能有关的东西都会被 JIT 处理)

至于性能,究竟是什么让您产生了“开销有基线”的疯狂想法?没有。从来没有,也永远不会。不在 C++ 和 Java 之间,也不在 Python 和 Javascript 或 任何 其他两种语言之间。有些事情你的特定版本的 JVM 会比你的特定 C++ 编译器做得更快,而你的特定 C++ 编译器会比你的特定 JVM 做得更好。

因此,您选择语言的“开销”完全取决于 1)您希望代码做什么,以及 2)您如何编写代码。

如果你把一个 Java 程序翻译成 C++,结果几乎肯定会运行得更慢。

如果你将一个 C++ 程序翻译成 Java,它的运行速度也会变慢。

不是因为一种语言比另一种语言“更快”,而是因为原始程序是为一种语言编写的,并且经过量身定制以在那种语言中运行良好。任何将其翻译成另一种语言的尝试都将失去这一优势。您最终会得到一个 C++ 风格的 Java 程序,它不会在 JVM 上高效运行,或者一个 Java 风格的 C++ 程序,它也将运行非常糟糕

两种语言规范都不包含“并且结果必须至少比语言 y 慢 x%”的子句。您的 C++ 编译器和 JVM 都尽最大努力使事情进展顺利。

然后您今天看到的性能特征可能会在明天发生变化。语言没有速度。

但要回答您的具体问题:

使用解释器时必须有一些开销基线。是否有一些一般的经验法则要记住? 10% 15%?我偶尔读过一篇博客,说 Java 代码几乎和本机代码一样快,但我可能有偏见。

如上所述,这取决于。对于许多常见任务,您通常不会看到超过百分之几的差异。对于某些用例,您会看到更大的差异(无论哪种方式。两种语言在性能方面都有优势。有一些与 JVM 相关的开销,但也有巨大的优化机会,尤其是垃圾收集器)

JVM 垃圾收集器是否会显着增加运行时性能的开销?我知道 Cocoa 应用程序已经开始使用垃圾收集模型,我同意它使编程变得更简单,但代价是什么?

基本上没有。平均而言,垃圾收集器比手动内存管理,原因有很多:

  • 在托管堆上,动态分配可以更快地完成
  • 可以以可忽略不计的摊销成本处理共享所有权,在本机语言中,您必须使用非常昂贵的引用计数
  • 在某些情况下,对象销毁也得到了极大的简化(大多数 Java 对象可以通过对内存块进行 GC 来回收。在 C++ 中,必须始终执行析构函数,并且几乎每个对象都有一)

GC 的主要问题是,虽然平均而言垃圾收集器的性能更好,但您会失去对 when 的一些控制以承担性能成本。手动内存管理可确保您的线程在等待清理内存时永远不会停止。垃圾收集器几乎可以在任何时候决定暂停进程并清理内存。在几乎所有情况下,这都足够快,不会有问题,但对于重要的实时数据来说,这是一个问题。

(另一个问题是你失去了一点表达能力。在 C++ 中,RAII 用于管理各种资源。在 Java 中,你不能使用 RAII。而是 GC 为你处理内存,并且为所有其他资源,你搞砸了,必须自己做很多尝试/最终块。没有理由不能用 GC 语言实现 RAII,但它在 Java 或 C# 中都不可用)

从 Java 进行系统调用的开销是多少?例如创建一个 Socket 对象,而不是 C 套接字 API。

大致相同。为什么会不一样?当然,Java 必须调用相关的 OS 服务和 API,因此会有一点点开销,但您可能不会注意到。

最后,我记得在某处读到 JVM 实现是单线程的。如果这是真的(我对此表示怀疑),这是否意味着 Java 线程真的不是真正的线程?一般来说,Java 线程是否对应于内核提供的底层线程? Java 应用程序是否像本机应用程序从多核/多 CPU 中受益一样?

Java 可以使用多个线程,是的。 JVM 本身 可能是单线程的(从某种意义上说,所有的 JVM 服务都运行在同一个线程上),我对此一无所知。但是您的 Java 应用程序可以使用任意数量的线程,并且它们被映射到操作系统线程并且将使用多个内核。

【讨论】:

  • "如果你把一个 Java 程序翻译成 C++,结果几乎肯定会运行得更慢。"就比较而言,我在这里的想法是创建一些使用每种语言最基本功能的简单参考程序(例如,可能是一个简单的循环对原语进行计算)。由于解释步骤,我假设在 JVM 中执行类似的数学表达式时必须存在一些恒定的开销,而不是。感谢您提供非常丰富的回复。
  • 需要注意的一点是我们这里所说的“哪个JVM实现”。
  • “平均而言,垃圾收集器比手动内存管理快得多”具有误导性。它比简单的内存分配要快,而且比优化的手动内存管理要慢得多。问题是没有人有时间进行优化,在优化模型中编程也不是很有趣
  • 还有更少的时间花在算法改进上。
  • @jalf “在托管堆上,动态分配可以更快地完成”。这一点让我很感兴趣。我很想知道为什么。这个问题很老了,但你能详细说明一下吗?总体来说是一个非常好的答案。
【解决方案2】:

java 和 c#(和 Objective-c)都没有本地代码那么快。但这仅在您遇到不受工程时间限制的问题时才重要。因为您将有时间用高级语言设计更好的算法。

因此,基本上,如果您正在开发一种每年要制造一百万的设备,或者是电池供电的设备,那么您不会使用 java 或 c# 来构建其核心功能。不过,您可以添加一个 lisp 解释器来简化定制。微软不会使用 c# 来表示 SQL Server 的核心,因为性能真的很重要。另一方面,MS 可以期望用户拥有高端硬件的 Visual Studio 可以用作展示速度慢但生产力高的技术。

请注意,我目前的大部分编程都是在 Pharo Smalltalk 中完成的,它比 java、c# 或 Objective-c 慢很多,甚至不是最快的 Smalltalks 之一。生产力胜过性能。

【讨论】:

  • 在很多地方,JIT 实际上可以将 java 代码编译成比普通 C 或 C++ 编译器预编译的本机代码更快的本机代码。通常原因是它有关于如何使用代码的确切信息,可以进行逃逸分析等。您的整体性能是好是坏很大程度上取决于应用程序的类型。
  • @Stephan 有趣,你有这个的来源吗?
  • 你是说SQL Server不能运行在“高端硬件”上?
  • 在 c++ 中,您有更多的可能性来微调代码。最终结果(有足够的工程时间)在 c++ 中会更快。 JVM 优化越来越好,但还没有。
  • 不,SQL Server 对性能至关重要,而 Visual Studio 不是
【解决方案3】:
【解决方案4】:

解决您的每个问题:

  • 解释代码的开销远高于 10-15%(我猜大概是 3x-5x 或更高)。为了降低到 10-15%,您必须使用某种形式的机器代码编译步骤(即 JIT)。 (尝试在关闭 JIT 的情况下运行 JVM,您会发现性能下降得像石头一样。)
  • 垃圾收集确实会影响性能,但我想说每个人都同意这是值得的。如果您能负担得起字节码编译/解释开销,那么您也可以负担 gc 开销。
  • Java 中的套接字编程比 C/C++ 中的要容易得多,如果这就是您的要求的话。在性能方面,套接字 I/O 开销超过了 Java 执行开销。
  • 大多数现代 JVM 都有真正的线程,即每个 Java 线程都由一个内核线程执行,从而允许 Java 线程利用现代多核 CPU。

【讨论】:

  • 说真的,你们是从哪里得到这些数字的?
  • 有经验吗?基于几年实施硬件模拟器的有根据的猜测?不过,我个人已经有几年没有对解释过的 JVM 代码进行任何基准测试了。如果您有任何硬数据,请随时站出来。
  • 垃圾回收通常会对性能产生积极的影响。分配和垃圾收集对象的摊销成本远低于手动内存管理的成本。
  • 解释和JIT不一样。它们是 JVM 的两种不同的操作模式,将它们混合在一起不会有任何好处。
  • 在这种情况下,我认为您的假设完全是错误的-问题实际上只是通常的 JVM 与本机问题。
【解决方案5】:

很多人低估了java的性能。我曾经也对此感到好奇,并在 java 中编写了一个简单的程序,然后在 c 中编写了一个等效程序(只不过是使用 for 循环和一个庞大的数组进行一些操作)。我不记得确切的数字,但我知道当 c 程序没有使用任何优化标志(在 gcc 下)编译时,java 击败了 c。正如预期的那样,当我最终通过积极的优化编译它时,c 领先了。老实说,这绝不是一个科学实验,但它确实让我知道了 java 的位置。

当然,当你开始做需要系统调用的事情时,java 可能会更落后。不过,我已经看到了 100MB/s 的磁盘和网络读取性能以及在普通硬件上运行的 java 程序。不知道这到底是什么意思,但它确实向我表明它对于我需要它的几乎所有东西来说已经足够了。

至于线程,如果你的java程序创建了2个线程,那么你就有2个真正的线程。

【讨论】:

  • 是的,我在想我可以编写一个简单的比较程序,但后来我认为 SO 上的某个人会做一些更彻底的事情,这让我想到了这个问题。我对 Java 没有性能方面的抱怨;它非常适合我感兴趣的项目类型。我只是想获得它的相对性能基准。
  • “老实说,这绝不是一个科学实验,但它确实给了我一个了解 java 所处位置的基线。”不,实际上没有,它只是给了你一个概括的借口。
  • 嗯,当然。我并不是想说我的小试验是最终的答案,它明确地将 java 的各个方面的性能与 c 进行了比较。我只是在这个问题上给出了我自己的个人经验(我继续讨论这篇文章的其余部分)。
【解决方案6】:

由于您的目标非常温和“我希望了解性能受到的影响......”您应该能够通过检查中显示的程序和测量来实现大部分目标 strong>计算机语言基准游戏。

如您所知,Java 和 C++

但你必须思考whether measurements of tiny programs can plausibly indicate the likely performance of your application

【讨论】:

    【解决方案7】:

    实际上,基于仅在运行时可用的信息,VM 可以在运行时进行很多优化,而 C/C++ 编译器则无法做到。因此,在大多数情况下,JVM 至少会与本机程序一样快。

    Brian Goetz 在Towards a universal VM 的演讲中回答了您的大部分问题,如果不是全部的话。

    【讨论】:

    • 大多数情况?几乎不。在许多情况下,JVM 对于实际用途来说足够快,但是当原始 CPU 性能至关重要时,JVM 无法与精心设计的 C/C++ 程序相匹敌。这里的重点是,在很多情况下,原始 CPU 性能并不重要。
    • 这正是我的观点。在大多数情况下,原始 CPU 性能并不重要。
    • “大多数情况”不是真的。由于 JIT'er,JVM 有 潜力 更快,但在实践中,通常并非如此。尤其是因为虽然 JIT 编译器有很多有用的运行时信息可供其使用以实现更好的优化,但它通常没有时间使用它们。 C++ 编译器在构建期间可能需要一个小时来优化程序。 JIT 编译器必须在几分之一秒内完成。
    • “C/C++ 编译器无法做到的”基于 Google 配置文件的优化 c++
    【解决方案8】:

    需要注意的是

    Java 字节码 JIT 编译为针对特定硬件的更优化的代码

    针对通用硬件编译和优化的 C 代码,因此 它无法利用特定硬件提供的功能

    【讨论】:

    • 完全错误。您可以轻松地针对特定硬件优化 C 代码。最坏的情况是,您可以拥有一个函数的多个版本,并在运行时选择一个。其次,典型的 JIT 试图快速生成本地代码,而不是深度优化。您可以实现一个 JIT,它可以非常缓慢地编译为最佳机器代码,但实际上并没有完成。
    • @Mark Bessey:是的,与只有一个可分发项的 Java 相比,您可以将 c 优化到 N 个硬件以产生 N 个可分发项。就编译速度(产生非常优化的代码)而言,这是 JIT 的缺点。更多信息在这里en.wikipedia.org/wiki/Just-in-time_compilation
    • 抱歉以“这完全错了”开头 - 我心情不好。但无论如何,我已经看到/在足够多的虚拟机上工作过,我觉得“快速编译”方面总是比尝试生成最佳代码更重要。正如其他人之前指出的那样,如果你有一个现代编译器,你可以使用 Java 在 C++ 中理论上能够实现的相同类型的配置驱动优化。特别是在 Java 的情况下,JVM 字节码对于转换为机器代码非常不理想。
    • 错了!在 gcc 中,您可以打开特定的硬件功能并使用程序集。
    【解决方案9】:

    对此没有简单的答案。编写 C 风格的 C++ 是可能的(甚至是一个好主意),但是一旦您尝试在 C 中进行继承,事情就会变得丑陋。所以忽略 C 并使用 Java -vs- C++,因为它们彼此更接近。

    要真正了解它,您需要用两种语言以类似的方式编写两个相对较大的应用程序。如果你这样做,那么你是使用 STL 和 Java 集合类还是你自己编写并在语言之间移植它们?如果您使用本机,则取决于哪种实现更快,就好像您使用自己的实现一样,您并没有测试应用程序的真实速度。

    我会说您需要编写尽可能相似的应用程序,但在有意义的情况下使用特定于语言的库/习语。 C++ 和 Java 代码虽然相似,但有不同的做事方式——在 Java 中很容易的事情在 C++ 中可能非常困难,反之亦然。

    现代 GC 实现不会增加那么多开销,如果您愿意,可以切换到 GC in C++ 进行比较:-)

    Java 运行时可以做一些 C++ 编译器通常无法做到的事情,例如内联虚拟方法的能力。

    对于系统类型的事情,Java 通常诉诸于对 C 的调用,因此存在开销(尽管 JNI 比以前更快)。

    线程取决于实现。 Sun 曾经使用“绿色线程;对于 Solaris,但这早已不复存在。据我所知,大多数(全部?)现代 VM 使用本机线程。

    简而言之,我认为 Java -vs- C++ 的 % 开销没有一个很好的衡量标准,而且您发现的任何可能都是不代表真实世界的微基准(不幸的是)。

    【讨论】:

      【解决方案10】:

      现在多线程和分布式模式消除了对本机速度的需求,java 可以达到 c++ 的速度,并且在某些情况下可以更快,但总的来说,我们可以说 c++ 比 java 快一点,比如 10-20%,这可以通过在负载均衡器之后添加另一个服务来忽略,因此 4 个 c++ 服务可以做什么 5 个 java 服务可以以相同的速度执行,3 个 c++ 线程可以做什么 4 个 java 线程可以以相同的速度执行。 另一个重要的因素是代码本身,好的java代码可以比坏的c++代码更快,java有更大的社区和更强大的支持以及更稳定的库和工具。 所以对于服务器端 java 是更好的选择,可以节省时间并带来更稳定和高性能的代码

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-20
        • 1970-01-01
        • 2013-10-16
        • 2015-09-27
        相关资源
        最近更新 更多