当前位置:  开发笔记 > 编程语言 > 正文

C++性能与Java/C#

如何解决《C++性能与Java/C#》经验,为你挑选了9个好方法。

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

也许这更像是一个编译问题,而不是一个语言问题,但任何人都可以用简单的英语解释这些虚拟机语言之一如何比母语更好地执行?



1> paercebal..:

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指针......让我们给凯撒它的到期)将比在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(或其祖先,Managed 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(Ahem ...)和WCF(酷!)和WPF(酷!!!!两者都是通过XAML和原始C#.WPF非常容易,我相信Swing无法与之比较)和C#4.0.

结论是,虽然生成一个在C#/ Java中工作的代码比在C++中更容易/更快,但在C#中生成强大,安全且健壮的代码(在Java中甚至比在C++中更难)要比在C++中生成代码要困难得多.原因比比皆是,但可以归纳为:

    泛型不如模板那么强大(尝试编写一个有效的通用Parse方法(从字符串到T),或者在C#中有效等效的boost :: lexical_cast来理解问题)

    RAII仍然无法比拟(GC仍然可以泄漏(是的,我必须处理这个问题)并且只会处理内存.即使C#using也不是那么简单和强大,因为编写正确的Dispose实现很困难)

    C#readonly和Java final没有C++那么有用const(你无法在没有大量工作的情况下在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版本可能是最简单的实现,但最难分析性能.特别是垃圾收集的影响很复杂,很难调整."

资料来源:

https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf

http://www.computing.co.uk/ctg/news/2076322/-winner-google-language-tests

编辑(2011-09-20)

"Facebook上的消息是' 合理编写的C++代码运行得很快 ',这强调了优化PHP和Java代码所花费的巨大努力.矛盾的是,C++代码比其他语言更难编写,但高效的代码是[用C++编写比其他语言更容易]. "

- Herb Sutter at // build /,引用Andrei Alexandrescu的话

资料来源:

http://channel9.msdn.com/Events/BUILD/BUILD2011/TOOL-835T

http://video.ch9.ms/build/2011/slides/TOOL-835T_Sutter.pptx


希望



我已经看到了对这个
真实生活的影响



)
)
.
工作的东西时,它就是令人沮丧的语言.
您在5个月后编辑C#描述了我自己的经验(模板更好,更好,更好,RAII).+1.这三个仍然是我个人的C++(或D,我还没有时间)的杀手功能.

2> Orion Adrian..:

通常,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


有关此理论的例子,请参见http://shootout.alioth.debian.org/u32/which-programming-languages-are-fastest.php.
@Justicle你的c ++编译器为不同的架构提供的最好的东西通常是x86,x64,ARM等等.现在你可以告诉它使用特定的功能(例如SSE2),如果你很幸运,它甚至可以产生一些备份代码,如果那个功能不可用,但是它可以得到一个细粒度.当然没有专业化取决于缓存大小和诸如此类的东西.

3> Jon Norton..:

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

我的惨败让我感到羞耻吗?几乎不.托管代码几乎没有任何努力得到了非常好的结果.打败管理雷蒙德不得不:

写自己的文件I/O东西

写自己的字符串类

写自己的分配器

写自己的国际地图

当然,他使用可用的低级库来做到这一点,但这仍然是很多工作.你能打电话给STL计划吗?我不这么认为,我认为他保留了std :: vector类,这最终从来都不是问题,他保留了find函数.几乎其他一切都消失了.

所以,是的,你可以肯定击败CLR.我想,雷蒙德可以让他的节目更快.

有趣的是,两个程序内部计时器报告的解析文件的时间大致相同 - 每个30毫秒.不同之处在于开销.

对我来说,最重要的是,非托管版本需要6次修订才能击败托管版本,托管版本是原始非托管代码的简单端口.如果你需要最后一点性能(并且有时间和专业知识来获得它),你将不得不进行管理,但对我而言,我将在33版的第一个版本上获得数量级的优势.如果我尝试6次,我会获益.


链接已经死了,在这里找到了提到的文章:http://blogs.msdn.com/b/ricom/archive/2005/05/10/416151.aspx

4> 小智..:

特定CPU优化的编译通常被高估.只需要用C++编写一个程序,然后使用针对奔腾PRO的优化进行编译,然后在奔腾4上运行.然后使用针对奔腾4的优化重新编译.我通过长时间的下午通过几个程序来完成它.一般结果?? 通常性能提升不到2-3%.所以JIT的理论优势几乎没有.只有在使用标量数据处理功能时才能观察到大多数性能差异,这些功能最终需要手动微调以实现最佳性能.这种优化的执行速度慢且成本高,使得它们有时候不适合JIT.

在现实世界和实际应用程序中,C++通常仍然比java更快,主要是因为内存占用更少,从而提高了缓存性能.

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


您正在编译CPU优化并不是很多,但您正在编译运行时路径优化.如果您发现通常使用特定参数调用方法,则可以使用该参数预编译该例程作为常量,该常量可以(在控制流的布尔值的情况下)将巨大的工作量分解出来.C++无法接近进行这种优化.
@Bill我可能会混合两件事......但是在指令管道中运行时没有完成分支预测,实现了与语言无关的类似目标吗?

5> FlySwat..:

JIT(即时编译)可以非常快,因为它可以优化目标平台.

这意味着它可以利用CPU可以支持的任何编译器技巧,无论开发人员编写代码的CPU是什么.

.NET JIT的基本概念就像这样(大大简化):

第一次调用方法:

你的程序代码调用方法Foo()

CLR查看实现Foo()的类型并获取与之关联的元数据

从元数据中,CLR知道IL(中间字节代码)存储在哪个存储器地址中.

CLR分配一块内存,并调用JIT.

JIT将IL编译为本机代码,将其放入已分配的内存中,然后更改Foo()的类型元数据中的函数指针以指向此本机代码.

本机代码已运行.

第二次调用方法:

你的程序代码调用方法Foo()

CLR查看实现Foo()的类型,并在元数据中查找函数指针.

运行此内存位置的本机代码.

正如您所看到的,第二次,它与C++几乎完全相同,除了具有实时优化的优势.

也就是说,还有其他一些开销问题会降低托管语言的速度,但JIT可以提供很多帮助.



6> Euro Micelli..:

我喜欢猎户座阿德里安的回答,但还有另外一个方面.

几十年前,同样的问题涉及汇编语言与FORTRAN等"人类"语言.部分答案是类似的.

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

你必须逐个回答这个问题.

也就是说,我是C++的长期粉丝,我认为这是一种令人难以置信的表达和强大的语言 - 有时候被低估了.但在许多"现实生活"问题中(对我个人而言,这意味着"我得到的报酬"),C#将更快更安全地完成工作.

你支付的最大罚款?许多.NET和Java程序都是内存耗尽的.我看到.NET和Java应用程序需要"数百"兆字节的内存,而类似复杂程度的C++程序几乎不会划分"数十"的MB.



7> billjamesdev..:

我不确定你会发现Java代码运行得比C++更快的频率,即使使用Hotspot,但我会解释它是如何发生的.

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

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



8> Joel Coehoor..:

通常,程序的算法对应用程序的速度比语言更重要.您可以用任何语言实现一个糟糕的算法,包括C++.考虑到这一点,您通常能够使用帮助您实现更高效算法的语言更快地编写运行代码.

高级语言通过提供对许多有效的预构建数据结构的更容易访问以及鼓励可以帮助您避免低效代码的实践来做到这一点.当然,他们有时也可以轻松编写一堆非常慢的代码,因此您仍然需要了解您的平台.

此外,C++正在追赶"新"(注意引号)功能,如STL容器,自动指针等 - 例如,请参阅boost库.您有时可能会发现完成某项任务的最快方法需要一种像高级语言禁止的指针算术技术 - 尽管它们通常允许您调用以可以根据需要实现它的语言编写的库. .

最重要的是要了解您正在使用的语言,它是相关的API,它可以做什么,以及它的局限性.



9> Paul Nathan..:

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

推荐阅读
ar_wen2402851455
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有