119

我的理解是 C/C++ 生成在特定机器架构上运行的本机代码。相反,Java 和 C# 等语言在抽象出原生架构的虚拟机之上运行。从逻辑上讲,由于这个中间步骤,Java 或 C# 似乎不可能与 C++ 的速度相匹配,但是我被告知最新的编译器(“热点”)可以达到甚至超过这个速度。

也许这更像是一个编译器问题而不是语言问题,但是任何人都可以用简单的英语解释这些虚拟机语言中的一种如何可能比母语表现得更好吗?

4

31 回答 31

197

JIT 与静态编译器

如前文所述,JIT 可以在运行时将 IL/字节码编译为本机代码。提到了成本,但没有得出结论:

JIT 的一个大问题是它不能编译所有东西:JIT 编译需要时间,因此 JIT 只会编译部分代码,而静态编译器会生成完整的本地二进制文件:对于某些程序,静态编译器将轻松胜过 JIT。

当然,C#(或 Java,或 VB)通常比 C++ 更快地生成可行且健壮的解决方案(如果只是因为 C++ 具有复杂的语义,而 C++ 标准库虽然有趣且强大,但与完整的.NET 或 Java 标准库的范围),因此通常情况下,大多数用户看不到 C++ 和 .NET 或 Java JIT 之间的差异,对于那些关键的二进制文件,你仍然可以调用 C++ 处理来自 C# 或 Java(即使这种本机调用本身可能非常昂贵)......

C++ 元编程

请注意,通常,您将 C++ 运行时代码与 C# 或 Java 中的等效代码进行比较。但是 C++ 有一个开箱即用的特性可以胜过 Java/C#,那就是模板元编程:代码处理将在编译时完成(因此大大增加了编译时间),导致运行时为零(或几乎为零)。

我还没有看到这对现实生活的影响(我只玩过概念,但到那时,差别是 JIT 的执行秒数,而C++ 的执行时间为零),但值得一提的是,事实上模板元编程不是琐碎的...

编辑 2011-06-10:在 C++ 中,使用类型是在编译时完成的,这意味着生成调用非泛型代码的泛型代码(例如,从字符串到类型 T 的泛型解析器,为它识别的类型 T 调用标准库 API,并且使解析器易于由其用户扩展)非常简单且非常高效,而 Java 或 C# 中的等价物充其量写起来很痛苦,并且即使在编译时已知类型也总是会变慢并在运行时解析,这意味着您唯一的希望是让 JIT 内联整个事情。

...

编辑 2011-09-20: Blitz++(主页维基百科)背后的团队就是这样做的,显然,他们的目标是通过 C++ 模板元编程尽可能地从运行时执行转移到编译时间,从而达到 FORTRAN 在科学计算方面的性能. 所以我上面写的“我还没有看到对这个现实生活的影响”部分显然确实存在于现实生活中。

本机 C++ 内存使用

C++ 的内存使用与 Java/C# 不同,因此具有不同的优点/缺陷。

无论 JIT 优化如何,没有什么比直接指针访问内存更快(让我们暂时忽略处理器缓存等)。因此,如果您在内存中有连续的数据,通过 C++ 指针(即 C 指针......让我们给 Caesar 应有的)访问它会比在 Java/C# 中快几倍。C++ 有 RAII,这使得很多处理比 C# 甚至 Java 容易得多。C++ 不需要using限定其对象的存在范围。而 C++ 没有finally子句。这不是错误。

:-)

尽管有类似 C# 的原始结构,但 C++“在堆栈上”的对象在分配和销毁时不会花费任何成本,并且不需要 GC 在独立线程中工作来进行清理。

至于内存碎片,2008 年的内存分配器不是 1980 年的旧内存分配器,通常与 GC 相比:C++ 分配不能在内存中移动,没错,但是,就像在 Linux 文件系统上一样:谁需要硬盘当碎片没有发生时进行碎片整理?为正确的任务使用正确的分配器应该是 C++ 开发人员工具包的一部分。现在,编写分配器并不容易,然后,我们大多数人都有更好的事情要做,而且在大多数情况下,RAII 或 GC 已经足够好了。

编辑 2011-10-04:有关高效分配器的示例:在 Windows 平台上,从 Vista 开始,默认启用低碎片堆。对于以前的版本,可以通过调用 WinAPI 函数HeapSetInformation来激活 LFH 。在其他操作系统上,提供了替代分配器(参见https://secure.wikimedia.org/wikipedia/en/wiki/Malloc列表)

现在,随着多核和多线程技术的兴起,内存模型变得有些复杂。在这个领域,我猜想.NET 有优势,有人告诉我,Java 占据了上风。一些“裸机”黑客很容易称赞他的“接近机器”的代码。但是现在,手工生成更好的汇编比让编译器完成它的工作要困难得多。对于 C++ 来说,十年来编译器通常变得比黑客更好。对于 C# 和 Java,这更容易。

尽管如此,新标准 C++0x 将向 C++ 编译器强加一个简单的内存模型,这将标准化(从而简化)C++ 中有效的多处理/并行/线程代码,并使编译器的优化更容易和更安全。但是,我们将在几年后看到它的承诺是否成真。

C++/CLI 与 C#/VB.NET

注意:在本节中,我说的是 C++/CLI,即 .NET 托管的 C++,而不是原生 C++。

上周,我参加了 .NET 优化的培训,发现静态编译器无论如何都非常重要。与 JIT 一样重要。

在 C++/CLI(或其祖先,托管 C++)中编译的相同代码可能比在 C#(或 VB.NET,其编译器生成与 C# 相同的 IL)中生成的相同代码快几倍。

因为 C++ 静态编译器在生成已经优化的代码方面比 C# 的要好得多。

例如,.NET 中的函数内联仅限于字节码长度小于或等于 32 字节的函数。因此,C# 中的某些代码将生成一个 40 字节的访问器,它永远不会被 JIT 内联。C++/CLI 中的相同代码将生成一个 20 字节的访问器,该访问器将由 JIT 内联。

另一个例子是临时变量,它被 C++ 编译器简单地编译掉,但仍然在 C# 编译器生成的 IL 中被提及。C++ 静态编译优化将导致更少的代码,从而再次授权更积极的 JIT 优化。

推测这是因为 C++/CLI 编译器受益于 C++ 原生编译器的大量优化技术。

结论

我喜欢 C++。

但在我看来,C# 或 Java 都是更好的选择。不是因为它们比 C++ 快,而是因为当你将它们的质量相加时,它们最终会比 C++ 更有效率,需要更少的培训,并且拥有更完整的标准库。至于大多数程序,它们的速度差异(以一种或另一种方式)将可以忽略不计......

编辑 (2011-06-06)

我在 C#/.NET 上的经验

我现在有 5 个月的几乎独家专业 C# 编码(加起来我的简历已经充满了 C++ 和 Java,以及一点 C++/CLI)。

我玩过 WinForms(咳咳……)和 WCF(酷!)和 WPF(酷!!!!通过 XAML 和原始 C#。WPF 是如此简单,我相信 Swing 无法与之相比)和 C# 4.0。

结论是,虽然生成在 C#/Java 中运行的代码比在 C++ 中更容易/更快,但在 C# 中生成强大、安全和健壮的代码(在 Java 中甚至更难)比在 C++ 中要困难得多。原因很多,但可以概括为:

  1. 泛型不如模板强大尝试编写一个有效的泛型 Parse 方法(从字符串到 T),或 C# 中的 boost::lexical_cast 的有效等价物来理解问题
  2. RAII 仍然无与伦比GC 仍然会泄漏(是的,我不得不处理这个问题)并且只会处理内存。即使 C#using也没有那么简单和强大,因为编写正确的 Dispose 实现很困难
  3. C#readonly和 Javafinal远不如 C++ 有用const如果没有大量工作,就无法在 C# 中公开只读的复杂数据(例如,节点树),而它是 C++ 的内置特性。不可变数据是一个有趣的解决方案,但并非所有内容都可以变得不可变,因此到目前为止还不够)。

因此,只要您想要一些可以工作的东西,C# 仍然是一种令人愉快的语言,但当您想要一些始终安全地工作的东西时,C# 仍然是一种令人沮丧的语言。

Java 更令人沮丧,因为它与 C# 有相同的问题,而且还有更多问题:由于缺少 C#using关键字的等效项,我的一位非常熟练的同事花费了太多时间来确保正确释放其资源,而 C++ 中的等效项则有很容易(使用析构函数和智能指针)。

所以我想 C#/Java 的生产力提升对于大多数代码都是可见的……直到你需要代码尽可能完美的那一天。那天,你会知道痛苦。(你不会相信我们的服务器和 GUI 应用程序会问什么......)。

关于服务器端 Java 和 C++

我与大楼另一边的服务器团队保持联系(我在其中工作了 2 年,然后回到 GUI 团队),我学到了一些有趣的东西。

去年的趋势是 Java 服务器应用程序注定要取代旧的 C++ 服务器应用程序,因为 Java 有很多框架/工具,并且易于维护、部署等。

...直到最后几个月低延迟问题浮出水面。然后,Java 服务器应用程序,无论我们熟练的 Java 团队尝试如何优化,都简单而干净地输掉了与旧的、未真正优化的 C++ 服务器的竞争。

目前,决定将 Java 服务器保留用于性能仍然很重要的通用用途,不关心低延迟目标,并积极优化已经更快的 C++ 服务器应用程序以满足低延迟和超低延迟需求。

结论

没有什么比想象的那么简单。

Java,甚至更多的 C#,都是很酷的语言,具有广泛的标准库和框架,您可以在其中快速编码,并且很快就会有结果。

但是,当您需要原始功能、强大和系统的优化、强大的编译器支持、强大的语言功能和绝对安全时,Java 和 C# 使您难以赢得最后缺少但至关重要的质量百分比,您需要保持在竞争中的领先地位。

就好像你在 C#/Java 中比在 C++ 中需要更少的时间和更少经验的开发人员来生成平均质量的代码,但另一方面,当你需要优秀到完美的质量代码时,获得结果突然变得更容易和更快就在 C++ 中。

当然,这是我自己的看法,或许仅限于我们的具体需求。

但是,这仍然是今天发生的事情,无论是在 GUI 团队还是在服务器端团队中。

当然,如果有新情况发生,我会更新这篇文章。

编辑 (2011-06-22)

“我们发现,在性能方面,C++ 在很大程度上胜出。然而,它也需要最广泛的调优工作,其中许多工作的复杂程度是普通程序员无法做到的。

[...] Java 版本可能是最容易实现的,但最难分析性能。特别是垃圾收集的影响是复杂的,很难调整。”

资料来源:

编辑 (2011-09-20)

“Facebook 的流行语是‘合理编写的 C++ 代码运行速度很快’,这突显了在优化 PHP 和 Java 代码方面付出的巨大努力。矛盾的是,C++ 代码比其他语言更难编写,但高效的代码是[用 C++ 编写比用其他语言编写] 容易得多。

Herb Sutter//build/引用Andrei Alexandrescu

资料来源:

于 2008-09-28T09:35:40.360 回答
178

通常,C# 和 Java 可以同样快或更快,因为 JIT 编译器——一种在第一次执行 IL 时编译你的编译器——可以进行 C++ 编译程序无法进行的优化,因为它可以查询机器。可以判断机器是Intel还是AMD;Pentium 4、Core Solo 或 Core Duo;或者是否支持 SSE4 等。

一个 C++ 程序必须预先编译,通常使用混合优化,以便它在所有机器上运行得很好,但没有像针对单个配置(即处理器、指令集、其他硬件)那样优化。

此外,某些语言功能允许 C# 和 Java 中的编译器对您的代码做出假设,从而允许它优化某些部分,这对于 C/C++ 编译器来说是不安全的。当您可以访问指针时,会有很多不安全的优化。

此外,Java 和 C# 可以比 C++ 更有效地进行堆分配,因为垃圾收集器和代码之间的抽象层允许它一次完成所有堆压缩(相当昂贵的操作)。

现在我不能在下一点上为 Java 说话,但我知道例如 C# 在知道方法体为空时实际上会删除方法和方法调用。它会在你的代码中使用这种逻辑。

如您所见,某些 C# 或 Java 实现更快的原因有很多。

综上所述,可以在 C++ 中进行特定的优化,这将摧毁你可以用 C# 做的任何事情,尤其是在图形领域和任何你接近硬件的时候。指针在这里创造了奇迹。

因此,根据您要写的内容,我会选择其中一个。但是,如果您正在编写不依赖于硬件的东西(驱动程序、视频游戏等),我不会担心 C# 的性能(同样不能谈论 Java)。它会做得很好。

在 Java 方面,@Swati指出了一篇好文章:

https://www.ibm.com/developerworks/library/j-jtp09275

于 2008-09-28T03:21:49.930 回答
48

每当我谈到托管与非托管性能时,我喜欢指出 Rico(和 Raymond)所做的系列比较 C++ 和 C# 版本的中文/英文字典。这个谷歌搜索会让你自己阅读,但我喜欢 Rico 的总结。

所以我为自己的惨败感到羞耻吗?几乎不。托管代码几乎不费吹灰之力就获得了非常好的结果。要击败受管理的雷蒙德必须:

  • 自己写文件I/O的东西
  • 编写自己的字符串类
  • 编写自己的分配器
  • 编写自己的国际地图

当然,他使用了可用的较低级别的库来执行此操作,但这仍然需要大量工作。你能把剩下的称为 STL 程序吗?我不这么认为,我认为他保留了 std::vector 类,这最终从来都不是问题,并且他保留了 find 函数。几乎所有其他东西都没有了。

所以,是的,你绝对可以击败 CLR。我认为雷蒙德可以让他的程序运行得更快。

有趣的是,两个程序内部计时器报告的解析文件的时间大致相同——每个为 30 毫秒。区别在于开销。

对我来说,最重要的是,非托管版本需要 6 次修订才能击败作为原始非托管代码的简单移植的托管版本。如果你需要最后一点的性能(并且有时间和专业知识来获得它),你将不得不去管理,但对我来说,我将利用我在第一个版本中超过 33 的数量级优势% 如果我尝试 6 次,我将获得收益。

于 2008-10-21T02:34:20.820 回答
26

特定 CPU 优化的编译通常被高估了。只需用 C++ 编写一个程序,并使用针对 pentium PRO 的优化进行编译,然后在 pentium 4 上运行。然后使用针对 pentium 4 的优化重新编译。我用几个程序完成了漫长的下午。一般结果??通常性能提升不到 2-3%。所以理论上的 JIT 优势几乎没有。大多数性能差异只能在使用标量数据处理功能时才能观察到,最终需要手动微调才能实现最大性能。这种优化执行起来很慢而且成本很高,因此有时无论如何都不适合 JIT。

在现实世界和实际应用程序中,C++ 通常仍然比 Java 快,主要是因为内存占用更轻,从而导致更好的缓存性能。

但是要使用所有 C++ 功能,开发人员必须努力工作。你可以取得优异的成绩,但你必须为此动用你的大脑。C++ 是一种决定为您提供更多工具的语言,并收取您必须学习它们才能很好地使用该语言的费用。

于 2008-09-30T16:36:50.580 回答
21

JIT(即时编译)可以非常快,因为它针对目标平台进行了优化。

这意味着它可以利用您的 CPU 可以支持的任何编译器技巧,无论开发人员在哪个 CPU 上编写代码。

.NET JIT 的基本概念是这样工作的(高度简化):

第一次调用方法:

  • 您的程序代码调用方法 Foo()
  • CLR 查看实现 Foo() 的类型并获取与其关联的元数据
  • 从元数据中,CLR 知道 IL(中间字节码)存储在哪个内存地址中。
  • CLR 分配一块内存,并调用 JIT。
  • JIT 将 IL 编译为本机代码,将其放入分配的内存中,然后更改 Foo() 的类型元数据中的函数指针以指向该本机代码。
  • 运行本机代码。

第二次调用方法:

  • 您的程序代码调用方法 Foo()
  • CLR 查看实现 Foo() 的类型并在元数据中找到函数指针。
  • 运行此内存位置的本机代码。

如您所见,第二次,它几乎与 C++ 相同,除了具有实时优化的优势。

也就是说,还有其他开销问题会减慢托管语言的速度,但 JIT 有很大帮助。

于 2008-09-28T03:30:49.007 回答
12

我喜欢Orion Adrian的回答,但它还有另一个方面。

几十年前就汇编语言与像 FORTRAN 这样的“人类”语言提出了同样的问题。部分答案是相似的。

是的,C++ 程序在任何给定的(非平凡的?)算法上都能够比 C# 更快,但是 C# 中的程序通常会比 C++ 中的“幼稚”实现和 C++ 中的优化版本一样快或更快将需要更长的时间来开发,并且可能仍然以非常小的优势击败 C# 版本。那么,真的值得吗?

您必须逐一回答这个问题。

也就是说,我长期以来一直是 C++ 的粉丝,我认为它是一种极具表现力和强大的语言——有时被低估了。但在许多“现实生活”问题中(对我个人而言,这意味着“我得到报酬解决的那种”),C# 将更快、更安全地完成工作。

你支付的最大罚款?许多 .NET 和 Java 程序都占用大量内存。我见过 .NET 和 Java 应用程序占用“数百”兆字节的内存,而具有类似复杂性的 C++ 程序几乎无法占用“数十”兆字节的内存。

于 2008-09-28T05:40:28.803 回答
7

我不确定您是否经常会发现 Java 代码比 C++ 运行得更快,即使使用 Hotspot 也是如此,但我会稍微解释一下它是如何发生的。

将编译后的 Java 代码视为 JVM 的解释机器语言。当 Hotspot 处理器注意到某些已编译代码将被多次使用时,它会对机器代码执行优化。由于手动调整汇编几乎总是比 C++ 编译代码快,因此可以确定以编程方式调整的机器代码不会糟糕。

因此,对于高度重复的代码,我可以看到 Hotspot JVM 在哪里可以比 C++ 更快地运行 Java ......直到垃圾收集开始发挥作用。:)

于 2008-09-28T03:28:06.327 回答
6

一般来说,程序的算法对应用程序的速度比语言更重要。您可以用任何语言(包括 C++)实现糟糕的算法。考虑到这一点,您通常可以用一种可以帮助您实现更高效算法的语言编写运行速度更快的代码。

高级语言在这方面做得很好,它提供了对许多有效的预构建数据结构的更容易访问,并鼓励有助于避免低效代码的实践。当然,它们有时也可以很容易地编写一堆非常慢的代码,所以你仍然需要了解你的平台。

此外,C++ 正在赶上“新”(注意引号)特性,如 STL 容器、自动指针等——例如,参见 boost 库。你可能偶尔会发现完成某项任务的最快方法需要像指针算术这样的技术,这在高级语言中是被禁止的——尽管它们通常允许你调用一个可以根据需要实现它的语言编写的库.

最主要的是要知道你正在使用的语言,它的相关 API,它可以做什么,以及它的限制是什么。

于 2008-09-28T04:10:40.563 回答
5

我也不知道...我的 Java 程序总是很慢。:-) 不过,我从来没有真正注意到 C# 程序特别慢。

于 2008-10-21T04:05:57.110 回答
4

这是另一个有趣的基准测试,您可以在自己的计算机上尝试一下。

它比较了 ASM、VC++、C#、Silverlight、Java 小程序、Javascript、Flash (AS3)

Roozz 插件速度演示

请注意,javascript 的速度因执行它的浏览器而异。Flash 和 Silverlight 也是如此,因为这些插件与托管浏览器在同一进程中运行。但是 Roozz 插件运行标准的 .exe 文件,这些文件在自己的进程中运行,因此速度不受托管浏览器的影响。

于 2009-05-16T19:41:36.873 回答
4

您应该定义“表现优于......”。好吧,我知道,您问的是速度,但这并不是最重要的。

  • 虚拟机是否执行更多的运行时开销?是的!
  • 他们吃更多的工作记忆吗?是的!
  • 他们是否有更高的启动成本(运行时初始化和 JIT 编译器)?是的!
  • 他们需要安装一个庞大的库吗?是的!

等等,它有偏见,是的;)

使用 C# 和 Java,您会为获得的东西付出代价(更快的编码、自动内存管理、大型库等)。但是你没有太多讨价还价的余地:要么拿走完整的包裹,要么什么都不买。

即使这些语言可以优化某些代码以比编译代码更快地执行,整个方法(恕我直言)效率低下。想象一下每天开着卡车开车 5 英里到你的工作场所!它很舒服,感觉很好,你很安全(极端崩溃区),在你踩油一段时间后,它甚至会和标准车一样快!为什么我们不都有一辆卡车开车上班?;)

在 C++ 中,你得到你所支付的,不多也不少。

引用 Bjarne Stroustrup:“C++ 是我最喜欢的垃圾收集语言,因为它产生的垃圾很少” 链接文本

于 2009-12-03T21:35:31.787 回答
3

从 Java 或 C# 编译器生成的可执行代码不会被解释——它会“及时”(JIT)编译为本机代码。因此,在执行期间第一次遇到 Java/C# 程序中的代码时,由于“运行时编译器”(又名 JIT 编译器)将字节码 (Java) 或 IL 代码 (C#) 转换为本机机器指令,因此会产生一些开销。但是,下次在应用程序仍在运行时遇到该代码时,会立即执行本机代码。这解释了一些 Java/C# 程序最初看起来很慢,但运行时间越长性能越好。一个很好的例子是 ASP.Net 网站。第一次访问该网站时,可能会有点慢,因为 C# 代码由 JIT 编译器编译为本机代码。

于 2008-09-28T03:23:21.923 回答
3

虚拟机语言不太可能胜过编译语言,但它们可以足够接近以至于没关系,因为(至少)以下原因(我在这里说 Java,因为我从未做过 C#)。

1/ Java 运行时环境通常能够检测频繁运行的代码片段并对这些部分执行即时 (JIT) 编译,以便将来它们以全编译速度运行。

2/ Java 库的大部分被编译,因此当您调用库函数时,您执行的是编译后的代码,而不是解释的。您可以通过下载 OpenJDK 查看代码(C 语言)。

3/ 除非您进行大量计算,否则程序运行的大部分时间都在等待来自非常慢(相对而言)人类的输入。

4/ 由于Java字节码的很多验证都是在加载类的时候完成的,所以运行时检查的正常开销大大减少了。

5/ 在最坏的情况下,性能密集型代码可以提取到编译模块并从 Java 调用(参见 JNI),以便它全速运行。

总之,Java 字节码永远不会胜过本机机器语言,但有一些方法可以缓解这种情况。Java 的一大优势(如我所见)是巨大的标准库和跨平台特性。

于 2008-09-28T03:27:09.357 回答
3

关于您提出的具体问题,这里有一些很好的答案。我想退后一步,看看更大的图景。

请记住,您的用户对您编写的软件速度的看法受许多其他因素的影响,而不仅仅是代码生成的优化程度。这里有些例子:

  • 手动内存管理很难正确执行(没有泄漏),更难有效执行(完成后不久就释放内存)。一般来说,使用 GC 更有可能生成一个管理内存的程序。您是否愿意为了超越 GC 努力工作并延迟交付您的软件?

  • 我的 C# 比我的 C++ 更容易阅读和理解。我还有更多方法可以让自己相信我的 C# 代码工作正常。这意味着我可以优化我的算法,降低引入错误的风险(用户不喜欢崩溃的软件,即使它很快就崩溃了!)

  • 我可以用 C# 比用 C++ 更快地创建我的软件。这样可以腾出时间来提高性能,并且仍然可以按时交付我的软件。

  • 用 C# 编写好的 UI 比 C++ 更容易,所以我更有可能在 UI 保持响应时将工作推到后台,或者在程序必须阻塞一段时间时提供进度或心跳 UI。这并没有让任何事情变得更快,但它让用户更乐于等待。

我所说的关于 C# 的一切都可能适用于 Java,我只是没有经验可以肯定地说。

于 2008-09-28T04:11:28.953 回答
3

如果你是一名学习 C++ 的 Java/C# 程序员,你会很想继续从 Java/C# 的角度思考,并将逐字翻译成 C++ 语法。在这种情况下,您只能获得前面提到的本机代码与解释/JIT 的优势。为了在 C++ 与 Java/C# 中获得最大的性能提升,您必须学会用 C++ 思考并专门设计代码以利用 C++ 的优势。

套用Edsger Dijkstra的话说:[你的第一语言] 使心灵无法恢复。
套用Jeff Atwood的话说:你可以用任何新的语言来写 [你的第一语言]。

于 2008-09-28T09:44:12.837 回答
3

最重要的 JIT 优化之一是方法内联。如果 Java 可以保证运行时的正确性,它甚至可以内联虚拟方法。这种优化通常不能由标准静态编译器执行,因为它需要对整个程序进行分析,这很难因为单独编译(相比之下,JIT 拥有所有可用的程序)。方法内联改进了其他优化,提供更大的代码块进行优化。

Java/C# 中的标准内存分配也更快,而释放 (GC) 也不会慢很多,但确定性较低。

于 2009-01-21T21:46:59.557 回答
3

Orion Adrian,让我倒置你的帖子,看看你的言论有多么没有根据,因为关于 C++ 也可以说很多。并且告诉 Java/C# 编译器优化掉空函数确实让你听起来好像你不是我的优化专家,因为 a) 为什么一个真正的程序应该包含空函数,除了非常糟糕的遗留代码,b) 那真的不是黑色和出血边缘优化。

除了这句话之外,您还公然抱怨指针,但 Java 和 C# 中的对象不是基本上像 C++ 指针一样工作吗?它们不能重叠吗?它们不能为空吗?C(和大多数 C++ 实现)具有 restrict 关键字,两者都有值类型,C++ 具有非空保证的值引用。Java 和 C# 提供什么?

>>>>>>>>>>

通常,C 和 C++ 可以同样快或更快,因为 AOT 编译器 - 一个在部署之前编译你的代码的编译器,一劳永逸地,在你的高内存多核构建服务器上 - 可以进行 C# 编译程序的优化不能,因为它有很多时间这样做。编译器可以判断机器是Intel还是AMD;Pentium 4、Core Solo 或 Core Duo;或者如果支持 SSE4 等,并且如果您的编译器不支持运行时调度,您可以通过部署一些专门的二进制文件自己解决这个问题。

AC# 程序通常在运行时编译,以便它在所有机器上运行良好,但没有针对单个配置(即处理器、指令集、其他硬件)进行尽可能多的优化,并且必须花费一些时间第一的。循环裂变、循环反转、自动矢量化、整个程序优化、模板扩展、IPO 等功能很难以不惹恼最终用户的方式完全解决。

此外,某些语言功能允许 C++ 或 C 中的编译器对您的代码做出假设,从而允许它优化某些部分,而这些部分对于 Java/C# 编译器来说是不安全的。当您无法访问泛型的完整类型 ID 或有保证的程序流程时,会有很多不安全的优化。

此外,C++ 和 C 只需增加一个寄存器增量即可一次执行许多堆栈分配,这对于垃圾收集器和代码之间的抽象层来说肯定比 Java 和 C# 分配更有效。

现在我不能在下一点上为 Java 说话,但我知道例如 C++ 编译器实际上会在知道方法体为空时删除方法和方法调用,它会消除常见的子表达式,它可能会尝试并重试为了找到最佳的寄存器使用,它不强制进行边界检查,它会自动矢量化循环和内部循环,并将内部反转为外部,将条件移出循环,拆分和取消拆分循环。它会将 std::vector 扩展为本地零开销数组,就像您使用 C 方式一样。它将进行程序间优化。它将直接在调用者站点构造返回值。它将折叠和传播表达式。它将数据重新排序为缓存友好的方式。它将执行跳转线程。它使您可以编写零运行时开销的编译时光线追踪器。它将进行非常昂贵的基于图的优化。它会降低强度,如果它用语法上完全不相等但语义上等效的代码替换某些代码(旧的“xor foo,foo”只是最简单的,尽管这种优化已经过时了)。如果您愿意的话,您可以省略 IEEE 浮点标准并启用更多优化,例如浮点操作数重新排序。在它按摩和屠杀你的代码之后,它可能会重复整个过程,因为通常,某些优化为更确定的优化奠定了基础。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。它是否用语法上完全不相等但语义上等效的代码替换了某些代码(旧的“xor foo, foo”只是最简单的,尽管这种优化已经过时了)。如果您愿意的话,您可以省略 IEEE 浮点标准并启用更多优化,例如浮点操作数重新排序。在它按摩和屠杀你的代码之后,它可能会重复整个过程,因为通常,某些优化为更确定的优化奠定了基础。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。它是否用语法上完全不相等但语义上等效的代码替换了某些代码(旧的“xor foo, foo”只是最简单的,尽管这种优化已经过时了)。如果您愿意的话,您可以省略 IEEE 浮点标准并启用更多优化,例如浮点操作数重新排序。在它按摩和屠杀你的代码之后,它可能会重复整个过程,因为通常,某些优化为更确定的优化奠定了基础。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。如果您愿意的话,您可以省略 IEEE 浮点标准并启用更多优化,例如浮点操作数重新排序。在它按摩和屠杀你的代码之后,它可能会重复整个过程,因为通常,某些优化为更确定的优化奠定了基础。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。如果您愿意的话,您可以省略 IEEE 浮点标准并启用更多优化,例如浮点操作数重新排序。在它按摩和屠杀你的代码之后,它可能会重复整个过程,因为通常,某些优化为更确定的优化奠定了基础。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。它也可能只是使用改组的参数重试,并查看其他变体在其内部排名中的得分情况。它会在你的代码中使用这种逻辑。

如您所见,某些 C++ 或 C 实现更快的原因有很多。

综上所述,可以在 C++ 中进行许多优化,这些优化将击败您可以使用 C# 做的任何事情,尤其是在数字运算、实时和接近金属的领域,但不仅限于此。你甚至不必触摸一个指针就能走很长一段路。

因此,根据您要写的内容,我会选择其中一个。但是,如果您正在编写不依赖于硬件的东西(驱动程序、视频游戏等),我不会担心 C# 的性能(同样不能谈论 Java)。它会做得很好。

<<<<<<<<<<

一般来说,某些笼统的论点在特定帖子中可能听起来很酷,但通常听起来并不一定可信。

无论如何,为了和平:AOT很棒,JIT也很棒。唯一正确的答案可能是:视情况而定。真正聪明的人知道,无论如何你都可以使用两全其美的东西。

于 2011-06-06T14:40:18.287 回答
2

只有当 Java 解释器生成的机器代码实际上比您的编译器为您正在编写的 C++ 代码生成的机器代码优化得更好时,才会发生这种情况,以至于C++ 代码比 Java 和解释成本慢。

然而,这种情况实际发生的可能性非常低——除非 Java 有一个编写得很好的库,并且你有自己编写的 C++ 库。

于 2008-09-28T03:21:28.823 回答
2

实际上,C# 并不像 Java 那样真正在虚拟机中运行。IL 被编译成汇编语言,完全是本机代码,运行速度与本机代码相同。您可以预先 JIT 一个完全消除 JIT 成本的 .NET 应用程序,然后您将运行完全本机代码。

.NET 的减速并不是因为 .NET 代码更慢,而是因为它在幕后做了很多事情,比如垃圾收集、检查引用、存储完整的堆栈帧等。这在以下情况下非常强大和有用构建应用程序,但也是有代价的。请注意,您也可以在 C++ 程序中执行所有这些操作(许多核心 .NET 功能实际上是您可以在 ROTOR 中查看的 .NET 代码)。但是,如果您手动编写相同的功能,您最终可能会得到一个慢得多的程序,因为 .NET 运行时已经过优化和微调。

也就是说,托管代码的优势之一是它可以完全可验证,即。您可以验证代码永远不会访问另一个进程的内存或在执行之前执行 unsage 事情。微软有一个完全托管操作系统的研究原型,令人惊讶地表明,通过利用此验证关闭托管程序不再需要的安全功能,100% 托管环境实际上可以比任何现代操作系统执行得更快。 (在某些情况下,我们说的是 10 倍)。SE电台有一个很棒的插曲谈论这个项目。

于 2008-10-21T03:21:38.883 回答
1

在某些情况下,托管代码实际上可以比本机代码更快。例如,“mark-and-sweep”垃圾收集算法允许 JRE 或 CLR 等环境在一次传递中释放大量短期(通常)对象,其中大多数 C/C++ 堆对象被一次性释放。一次。

来自维基百科

出于许多实际目的,在垃圾收集语言中实现的分配/解除分配密集型算法实际上可以比使用手动堆分配的等效算法更快。一个主要原因是垃圾收集器允许运行时系统以一种潜在的有利方式分摊分配和释放操作。

也就是说,我已经编写了很多 C# 和 C++,并且运行了很多基准测试。根据我的经验,C++ 在两个方面比 C# 快得多:(1) 如果您使用一些用 C# 编写的代码,将其移植到 C++,本机代码往往会更快。快多少?嗯,它变化很大,但看到 100% 的速度提升并不少见。(2) 在某些情况下,垃圾收集会大大降低托管应用程序的速度。.NET CLR 在处理大堆(例如,> 2GB)时做得很糟糕,最终可能会在 GC 上花费大量时间——即使在具有很少——甚至没有——中间生命周期对象的应用程序中也是如此。

当然,在我所遇到的大多数情况下,托管语言在很长一段时间内都足够快,并且为 C++ 的额外性能而进行的维护和编码权衡根本不是一个好的选择。

于 2008-09-28T03:38:25.057 回答
1

这是一个有趣的基准 http://zi.fi/shootout/

于 2008-10-16T13:21:59.390 回答
1

实际上 Sun 的 HotSpot JVM 使用“混合模式”执行。它解释方法的字节码,直到它确定(通常通过某种计数器)特定代码块(方法、循环、try-catch 块等)将被执行很多次,然后它 JIT 编译它。JIT 编译方法所需的时间通常比解释该方法(如果它是一个很少运行的方法)要长。“混合模式”的性能通常更高,因为 JVM 不会浪费时间 JITing 很少(如果有的话)运行的代码。C# 和 .NET 不这样做。.NET JIT 通常会浪费时间。

于 2008-10-21T04:32:48.863 回答
1

这是 Cliff Click 的答案:http ://www.azulsystems.com/blog/cliff/2009-09-06-java-vs-c-performanceagain

于 2009-09-10T19:14:02.777 回答
1

阅读有关 HP Labs 的Dynamo的信息,这是一种在 PA-8000 上运行的 PA-8000 解释器,它运行程序的速度通常比它们在本地运行时更快。那么它似乎一点也不奇怪!

不要将其视为“中间步骤”——运行一个程序已经涉及许多其他步骤,任何语言都可以。

它通常归结为:

  • 程序有热点,所以即使你在运行 95% 的代码主体时速度较慢,如果你在热点 5% 处速度更快,你仍然可以在性能上具有竞争力

  • HLL 比 C/C++ 之类的 LLL 更了解您的意图,因此可以生成更优化的代码(OCaml 甚至更多,实际上通常更快)

  • JIT 编译器有很多静态编译器没有的信息(比如,你这次碰巧拥有的实际数据)

  • JIT 编译器可以在运行时进行传统链接器实际上不允许做的优化(例如重新排序分支,以便常见情况是平坦的,或内联库调用)

总而言之,C/C++ 在性能方面是相当糟糕的语言:关于数据类型的信息相对较少,没有关于数据的信息,也没有动态运行时允许在运行时优化方面做很多事情。

于 2009-12-29T18:12:51.620 回答
1

当 Java 或 CLR 比 C++ 快时,您可能会遇到短暂的爆发,但总体而言,在应用程序的生命周期内性能更差:有关该结果的一些结果,请参见 www.codeproject.com/KB/dotnet/RuntimePerformance.aspx。

于 2010-07-09T17:50:27.980 回答
1

我的理解是 C/C++ 生成在特定机器架构上运行的本机代码。相反,Java 和 C# 等语言在抽象出原生架构的虚拟机之上运行。从逻辑上讲,由于这个中间步骤,Java 或 C# 似乎不可能与 C++ 的速度相匹配,但是我被告知最新的编译器(“热点”)可以达到甚至超过这个速度。

这是不合逻辑的。使用中间表示不会固有地降低性能。例如,llvm-gcc 通过 LLVM IR(它是一个虚拟的无限寄存器机器)将 C 和 C++ 编译为本机代码,它实现了出色的性能(经常击败 GCC)。

也许这更像是一个编译器问题而不是语言问题,但是任何人都可以用简单的英语解释这些虚拟机语言中的一种如何可能比母语表现得更好吗?

这里有些例子:

  • 具有 JIT 编译的虚拟机有助于生成运行时代码(例如System.Reflection.Emit在 .NET 上),因此您可以使用 C# 和 F# 等语言即时编译生成的代码,但必须求助于用 C 或 C++ 编写相对较慢的解释器。例如,实现正则表达式。

  • 虚拟机的某些部分(例如写屏障和分配器)通常是用手工编码的汇编程序编写的,因为 C 和 C++ 不能生成足够快的代码。如果一个程序强调系统的这些部分,那么可以想象它的性能可能优于任何可以用 C 或 C++ 编写的程序。

  • 本机代码的动态链接需要符合 ABI,这可能会阻碍性能并避免整个程序优化,而链接通常在 VM 上延迟,并且可以从整个程序优化中受益(如 .NET 的具体泛型)。

我还想解决上面 paercebal 高度赞成的答案的一些问题(因为有人不断删除我对他的答案的评论),这提出了适得其反的两极分化观点:

代码处理将在编译时完成...

因此,模板元编程仅在程序在编译时可用的情况下才有效,而情况通常并非如此,例如,不可能在 vanilla C++ 中编写具有竞争力的性能正则表达式库,因为它无法生成运行时代码(一个重要方面)元编程)。

...玩类型是在编译时完成的...Java 或 C# 中的等价物在最好的情况下编写起来很痛苦,并且即使在编译时已知类型也总是会变慢并在运行时解决。

在 C# 中,这仅适用于引用类型,不适用于值类型。

无论 JIT 优化如何,没有什么比直接指针访问内存更快...如果内存中有连续数据,通过 C++ 指针(即 C 指针...让我们给 Caesar 应得的)访问它会快几倍比在 Java/C# 中。

人们已经观察到Java 在 SciMark2 基准测试的 SOR 测试中击败 C++,正是因为指针阻碍了与别名相关的优化。

还值得注意的是,.NET 确实在链接后跨动态链接库对泛型进行类型特化,而 C++ 则不能,因为必须在链接之前解析模板。很明显,泛型相对于模板的最大优势是可理解的错误消息。

于 2011-09-06T10:34:47.393 回答
0

除了其他人所说的之外,根据我的理解,.NET 和 Java 更擅长内存分配。例如,它们可以在内存碎片化时压缩内存,而 C++ 则不能(本机,但如果您使用聪明的垃圾收集器,它可以)。

于 2008-09-28T03:26:52.243 回答
0

对于任何需要高速度的东西,JVM 只调用 C++ 实现,所以问题更多的是它们的库有多好,而不是 JVM 对于大多数与操作系统相关的东西有多好。垃圾收集将您的内存减半,但使用一些更出色的 STL 和 Boost 功能将产生相同的效果,但潜在的错误数倍。

如果您只是在具有许多类的大型项目中使用 C++ 库及其许多高级特性,那么您可能会比使用 JVM 慢。除了更容易出错。

但是,C++ 的好处是它允许您优化自己,否则您会被编译器/jvm 所做的事情所困扰。如果您制作自己的容器、编写自己的对齐内存管理、使用 SIMD 并在各处进行汇编,那么您可以将大多数 C++ 编译器自行执行的速度至少提高 2 到 4 倍。对于某些操作,16x-32x。那是使用相同的算法,如果您使用更好的算法并进行并行化,增长可能会非常惊人,有时比常用方法快数千倍。

于 2009-12-04T01:26:38.217 回答
0

我从几个不同的角度来看待它。

  1. 给定无限的时间和资源,托管或非托管代码会更快吗?显然,答案是非托管代码至少可以在这方面与托管代码联系在一起——因为在最坏的情况下,您只需对托管代码解决方案进行硬编码。
  2. 如果你把一个程序用一种语言直接翻译成另一种语言,它的性能会差多少?可能很多,对于任何两种语言。大多数语言需要不同的优化并且有不同的陷阱。微观性能通常与了解这些细节有关。
  3. 给定有限的时间和资源,两种语言中哪一种会产生更好的结果?这是最有趣的问题,因为虽然托管语言可能会产生稍慢的代码(给定为该语言合理编写的程序),但该版本可能会更快完成,从而有更多时间花在优化上。
于 2009-12-04T03:32:56.410 回答
0

一个非常简短的回答:在固定预算的情况下,您将获得比 C++ 应用程序性能更好的 Java 应用程序(ROI 考虑)此外,Java 平台有更多体面的分析器,这将帮助您更快地查明热点

于 2009-12-29T17:57:35.300 回答
0

我认为这些天不需要考虑服务器的处理速度(毕竟多核处理器都进入市场)。它应该根据内存使用情况进行更多定义。这样java就有了一个小缺点。
但总的来说,编程语言仍然适用于不同的目的。那是他们的竞争领域,在每个部分都是不同的赢家。
而且我确信 Java 将在长期内获胜,因为它会在它产生的所有新特性中继续发展和竞争力。
我在这里找到了一个链接,它将支持我投票给 java 的理由

于 2011-09-07T06:18:15.803 回答