我的理解是C / C ++会生成在特定机器体系结构上运行的本机代码。相反,诸如Java和C#之类的语言在虚拟机之上运行,该虚拟机抽象出本机体系结构。从逻辑上讲,由于这一中间步骤,Java或C#似乎不可能达到C ++的速度,但是有人告诉我最新的编译器(“热点”)可以达到或什至超过此速度。
也许这更像是编译器问题,而不是语言问题,但是谁能用通俗的英语解释这些虚拟机语言中的一种比本地语言有更好的表现?
我的理解是C / C ++会生成在特定机器体系结构上运行的本机代码。相反,诸如Java和C#之类的语言在虚拟机之上运行,该虚拟机抽象出本机体系结构。从逻辑上讲,由于这一中间步骤,Java或C#似乎不可能达到C ++的速度,但是有人告诉我最新的编译器(“热点”)可以达到或什至超过此速度。
也许这更像是编译器问题,而不是语言问题,但是谁能用通俗的英语解释这些虚拟机语言中的一种比本地语言有更好的表现?
Answers:
通常,C#和Java可以达到相同或更快的速度,因为JIT编译器(可以在第一次执行IL时对其进行编译的编译器)可以进行优化,而C ++编译的程序无法进行查询,因为它可以查询机器。它可以确定计算机是Intel还是AMD;奔腾4,Core Solo或Core Duo;或是否支持SSE4等
通常必须事先使用混合优化来编译C ++程序,这样它才能在所有机器上正常运行,但没有像单个配置(即处理器,指令集,其他硬件)那样优化。
另外,某些语言功能允许C#和Java的编译器对您的代码进行假设,从而使它们可以优化某些部分,而这对于C / C ++编译器而言是不安全的。当您可以访问指针时,有很多优化是不安全的。
而且,Java和C#可以比C ++更有效地进行堆分配,因为垃圾收集器和您的代码之间的抽象层允许它一次完成所有的堆压缩(这是相当昂贵的操作)。
现在,我不能再说Java了,但是我知道,例如C#在知道方法主体为空时实际上将删除方法和方法调用。并且它将在您的代码中使用这种逻辑。
如您所见,某些C#或Java实现会更快的原因很多。
综上所述,可以在C ++中进行特定的优化,这将使您无法使用C#进行的所有工作,特别是在图形领域以及您接近硬件时。指针在这里产生奇迹。
因此,根据您所写的内容,我会选择其中之一。但是,如果您编写的内容与硬件无关(驱动程序,视频游戏等),那么我就不必担心C#的性能(再也不能说Java)。会很好的。
在Java方面,@ Swati指出了一篇不错的文章:
如前几篇文章所述,JIT可以在运行时将IL /字节码编译为本机代码。提到了这样做的代价,但并未得出结论:
JIT有一个很大的问题是它无法编译所有内容:JIT编译需要时间,因此JIT将只编译部分代码,而静态编译器将生成完整的本机二进制文件:对于某些程序,静态编译器将轻易地胜过JIT。
当然,C#(或Java或VB)通常比C ++更快地产生可行且健壮的解决方案(如果仅仅是因为C ++具有复杂的语义,而C ++标准库虽然有趣且强大,但与完整版本相比却相当差劲)。 (.NET或Java标准库的范围),因此通常,大多数用户看不到C ++和.NET或Java JIT之间的差异,对于那些至关重要的二进制文件,您仍然可以调用C ++处理从C#或Java(即使这种本地调用本身可能会非常昂贵)...
请注意,通常,您正在将C ++运行时代码与其在C#或Java中的等效代码进行比较。但是C ++具有一个可以立即胜过Java / C#的功能,即模板元编程:代码处理将在编译时完成(因此,大大增加了编译时间),导致运行时为零(或几乎为零)。
我到目前为止还没有看到现实的效果(我只玩过概念,但是那时,JIT的执行时间为几秒钟,C ++ 的执行时间为零),但这是值得一提的,同时模板元编程不是不重要的...
编辑2011-06-10:在C ++中,类型的编译是在编译时完成的,这意味着生成泛型代码,该泛型代码将调用非泛型代码(例如,从字符串到类型T的泛型解析器,为其识别的类型T调用标准库API,并使解析器易于用户扩展)非常容易且非常有效,而Java或C#中的等效语言充其量是令人费解的,并且即使在编译时知道类型,也总是会变慢并在运行时解析,这意味着您唯一的希望是JIT可以对整个过程进行内联。
...
编辑2011-09-20: Blitz ++(主页,Wikipedia)背后的团队采用了这种方式,显然,他们的目标是通过C ++模板元编程从运行时执行到编译时尽可能多地转移,从而达到FORTRAN在科学计算上的性能。 。因此,我上面写的“我对此还没有看到现实生活的影响 ”部分显然确实存在于现实生活中。
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 。在其他OS上,提供了备用分配器(请参见https://secure.wikimedia.org/wikipedia/en/wiki/Malloc以获得列表)
现在,随着多核和多线程技术的兴起,内存模型变得有些复杂。在这个领域,我猜想.NET具有优势,而有人告诉我Java占据了上风。一些“裸机”黑客很容易赞美他的“机器附近”代码。但是现在,与让编译器正常工作相比,手工生成更好的汇编要困难得多。对于C ++,十年来,编译器通常变得比黑客更好。对于C#和Java,这甚至更容易。
尽管如此,新的标准C ++ 0x仍将为C ++编译器提供一个简单的内存模型,该模型将标准化(从而简化)C ++中有效的多处理/并行/线程化代码,并使编译器的优化工作更轻松,更安全。但是接下来,我们将在几年内看到其承诺是否兑现。
注意:在本节中,我谈论的是C ++ / CLI,即.NET托管的C ++,而不是本机C ++。
上周,我接受了有关.NET优化的培训,并且发现静态编译器仍然非常重要。比JIT重要。
用C ++ / CLI(或其祖先,托管C ++)编译的完全相同的代码可能比用C#(或VB.NET,其编译器生成的IL与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 ++生产力更高,需要的培训更少并且拥有更完整的标准库。对于大多数程序而言,它们的速度差异(以一种或另一种方式)可以忽略不计...
我现在有5个月的几乎完全专业的C#编码(这加起来我的CV已经充满了C ++和Java,以及一点C ++ / CLI)。
我玩过WinForms(Ahem ...),WCF(cool!)和WPF(Cool !!!!都通过XAML和原始C#玩过。WPF非常简单,我相信Swing不能与之相比)和C#4.0。
结论是,虽然在C#/ Java中生成的代码比在C ++中更容易/更快地生成,但是在C#中(比Java更加难)在Java中生成更强大,安全和健壮的代码要困难得多。原因很多,但可以归纳为:
using
也没有那么容易和强大,因为编写正确的Dispose实现很困难)readonly
和Java final
远没有C ++有用const
(如果没有C ++的内置功能,就无法在C#中公开只读复杂数据(例如,节点树),而C#是Java的内置功能。不可变数据是一种有趣的解决方案,但并非所有内容都可以设为不可变的,因此到目前为止)还不够。因此,只要您想要某种有效的东西,C#仍然是一种令人愉悦的语言,但是当您想要一种始终安全地起作用的东西时,C#就令人沮丧。
Java更加令人沮丧,因为它具有与C#相同的问题,并且存在更多问题:缺少C#的using
关键字,我的一个非常熟练的同事花了太多时间来确保正确释放其资源,而C ++中的等效项将具有很容易(使用析构函数和智能指针)。
因此,我想对于大多数代码来说C#/ Java的生产率提高都是可见的……直到有一天,您需要代码尽可能地完美。那天,你会知道痛苦的。(您不会相信我们的服务器和GUI应用程序的要求...)。
我在大楼的另一侧与服务器团队保持联系(在返回GUI团队之前,我在其中工作了2年),我学到了一些有趣的东西。
近年来,趋势是注定要用Java服务器应用程序替换旧的C ++服务器应用程序,因为Java有很多框架/工具,并且易于维护,部署等。
...直到低延迟的问题在过去的几个月里抬起了头。然后,无论我们熟练的Java团队尝试进行何种优化,Java服务器应用程序都简单而干净地失去了与未经过优化的旧C ++服务器的竞争。
当前,决定是将Java服务器保留在性能仍然很重要但不受低延迟目标关注的地方的通用服务器,并针对低延迟和超低延迟需求积极优化本已更快的C ++服务器应用程序。
没有什么比预期的那么简单。
Java甚至更多的C#是很酷的语言,具有广泛的标准库和框架,您可以在其中快速编写代码,并很快就能得到结果。
但是,当您需要原始能力,强大而系统的优化,强大的编译器支持,强大的语言功能以及绝对的安全性时,Java和C#很难赢得您需要保持在竞争之上的最后缺失但至关重要的质量百分比。
似乎与使用C ++相比,用C#/ Java花费更少的时间和更少的经验的开发人员来生成平均质量的代码,但是,另一方面,当您需要优秀的,完美的质量代码时,突然变得更容易,更快地得到结果。就在C ++中
当然,这是我自己的看法,可能仅限于我们的特定需求。
但是,无论今天在GUI团队还是在服务器端团队中,这都是今天发生的事情。
当然,如果发生新情况,我将更新此帖子。
“我们发现,就性能而言,C ++大获全胜。但是,它也需要最广泛的调整工作,其中许多工作都是在复杂的水平上完成的,而普通的程序员则无法使用。
[...] Java版本可能是最简单的实现,但最难分析其性能。具体来说,垃圾回收的影响非常复杂,很难调整。”
资料来源:
“ Facebook上流行的说法是,' 合理编写的C ++代码运行起来很快 ',这突显了优化PHP和Java代码所花费的巨大努力。自相矛盾的是,C ++代码比其他语言更难编写,但是高效的代码是(用C ++编写比用其他语言编写要容易得多)。 ”
资料来源:
每当我谈到托管性能与非托管性能时,我都想指出Rico(和Raymond)所做的系列比较中/英文字典的C ++和C#版本。这个Google搜索会让您自己阅读,但是我喜欢Rico的摘要。
那么,我为自己惨败而感到羞愧吗?几乎不。几乎无需付出任何努力,托管代码就获得了很好的结果。为了击败托管的雷蒙德,必须:
- 编写自己的文件I / O内容
- 编写自己的字符串类
- 编写自己的分配器
- 撰写自己的国际地图
当然,他使用可用的低级库来执行此操作,但这仍然是很多工作。您能称呼剩下的STL程序吗?我不这么认为,我认为他保留了std :: vector类,该类最终从不成问题,并且保留了find函数。几乎所有其他一切都消失了。
因此,是的,您绝对可以击败CLR。我认为Raymond可以使他的程序运行得更快。
有趣的是,两个程序内部计时器报告的解析文件的时间大致相同-每个计时器30毫秒。区别在于开销。
对我来说,最重要的是,非托管版本花了6个修订版才击败了托管版本,后者是原始非托管代码的简单移植。如果您需要性能的最后一点(并且有时间和专业知识来获得它),则必须不受管理,但是对我而言,我将采用第一个版本在33之上的数量级优势。如果尝试6次,我会有所收获。
用于特定CPU优化的编译通常被高估了。只需使用C ++编写一个程序,然后针对pentium PRO进行优化编译并在奔腾4上运行。然后针对pentium 4进行优化进行重新编译。一般结果??通常性能提升不到2-3%。因此,JIT的理论优势几乎没有。大多数性能差异仅在使用标量数据处理功能时才能观察到,而最终需要手动微调以达到最佳性能。这种优化的执行速度缓慢且成本高昂,因此有时仍然不适合JIT。
在现实世界和实际应用程序中,C ++通常仍比Java快,这主要是因为内存占用量更少,从而导致更好的缓存性能。
但是要使用所有C ++功能,开发人员必须努力工作。您可以取得优异的成绩,但是您必须为此动脑筋。C ++是一种决定为您提供更多工具的语言,收取一定的费用,您必须学习这些工具才能很好地使用该语言。
JIT(及时编译)的速度非常快,因为它针对目标平台进行了优化。
这意味着,无论开发人员在哪个CPU上编写代码,它都可以利用您的CPU支持的任何编译器技巧。
.NET JIT的基本概念如下所示(大大简化):
首次调用方法:
第二次调用方法:
正如您所看到的,第二次是它与C ++几乎相同的过程,只是具有实时优化的优势。
就是说,还有其他开销问题会减慢托管语言的速度,但是JIT很有帮助。
我喜欢Orion Adrian的答案,但它还有另一方面。
几十年前,关于汇编语言与“人类”语言(如FORTRAN)提出了相同的问题。答案的一部分是相似的。
是的,在任何给定的(非平凡的)算法上,C ++程序都能够比C#快,但是C#中的程序通常会比C ++中的“天真”实现以及C ++中的优化版本快或快。开发将花费更长的时间,并且可能仍会以很小的优势击败C#版本。那么,真的值得吗?
您必须一对一地回答该问题。
就是说,我一直是C ++的忠实拥护者,而且我认为它是一种令人难以置信的表达和强大的语言-有时被人们低估。但是在许多“现实生活”问题中(对我个人而言,这意味着“获得报酬来解决的那种”),C#将使工作更快,更安全地完成。
您付出的最大罚款?许多.NET和Java程序都是内存消耗。我已经看到.NET和Java应用程序占用“数百”兆字节的内存,而类似复杂性的C ++程序几乎不占用“数十”兆字节。
我不确定即使使用Hotspot,您会发现Java代码运行的频率也比C ++快的多,但是我会花一些时间解释它是如何发生的。
将已编译的Java代码视为JVM的解释机语言。当Hotspot处理器注意到某些已编译的代码将被多次使用时,它将对机器代码进行优化。由于手工调整大会几乎总是快于C ++编译的代码,这是确定的数字,编程,调整机器代码不会是太糟糕。
因此,对于高度重复的代码,我可以看到Hotspot JVM可以比C ++更快地运行Java ...直到垃圾回收开始发挥作用。:)
Since hand-tuning Assembly is almost always faster than C++ compiled code
吗?“手动调整程序集”和“ C ++编译代码”是什么意思?
通常,与语言相比,程序的算法对于应用程序的速度将更为重要。您可以使用任何语言(包括C ++)来实现较差的算法。考虑到这一点,通常您将能够使用一种可以帮助您实现更高效算法的语言来更快地编写运行代码。
高级语言通过提供对许多有效的预构建数据结构的更轻松访问以及鼓励可帮助您避免低效率代码的实践,在此方面做得很好。当然,有时它们也可以使编写一堆非常慢的代码变得容易,因此您仍然必须了解您的平台。
同样,C ++正在赶上STL容器,自动指针等“新”(请注意引号)功能-例如,参见boost库。您可能偶尔会发现,完成某些任务的最快方法需要高级语言中禁止的指针算术之类的技术,尽管它们通常允许您调出使用可以按需实现的语言编写的库。
最主要的是要知道您使用的语言,与之关联的API,它可以做什么以及它的局限性。
我也不知道...我的Java程序总是很慢。:-)不过,我从未真正注意到C#程序的运行速度特别慢。
这是另一个有趣的基准,您可以在自己的计算机上尝试一下。
它比较了ASM,VC ++,C#,Silverlight,Java applet,Javascript,Flash(AS3)
请注意,JavaScript的速度变化很大,具体取决于执行该浏览器的浏览器。Flash和Silverlight也是如此,因为这些插件与托管浏览器的运行过程相同。但是Roozz插件运行标准的.exe文件,这些文件以自己的进程运行,因此速度不受托管浏览器的影响。
您应该定义“性能优于..”。好吧,我知道,您问的是速度,但它并不是至关重要的。
依此类推,这是有偏见的,是;)
使用C#和Java,您需要付出一定的代价(更快的编码,自动内存管理,大型库等)。但是您没有太多余地来讨价还价:拿完整的包裹或者什么都不做。
即使那些语言可以优化某些代码以使其比编译后的代码执行得更快,但整个方法还是无效的。想象一下,每天用卡车开车到您的工作场所5英里!它舒适,感觉良好,很安全(极端的压溃区),踩油门一段时间后,它的速度甚至可以和标准车一样快!为什么我们所有人都没有卡车开车上班?;)
在C ++中,您得到的是所支付的,而不是更多,更少。
引用Bjarne Stroustrup的话:“ C ++是我最喜欢的垃圾收集语言,因为它产生的垃圾很少” 链接文本
times
在shell上重复您的基准测试。这样就可以检查整个程序,而不仅仅是一个方面。结果是否相似?
关于您所问的特定问题,这里有一些很好的答案。我想退后一步,看看大图。
请记住,用户对编写软件速度的看法受许多其他因素的影响,而不仅仅是代码生成的优化程度。这里有些例子:
手动内存管理很难正确执行(没有泄漏),甚至更难有效地执行(完成后不久释放内存)。通常,使用GC更有可能产生可以很好地管理内存的程序。您是否愿意努力工作,并延迟交付软件,以超越GC?
我的C#比我的C ++更易于阅读和理解。我还有更多方法可以使自己确信我的C#代码可以正常工作。这意味着我可以优化算法,减少引入错误的风险(并且用户不喜欢崩溃的软件,即使崩溃很快!)
与使用C ++相比,在C#中创建软件的速度更快。这样可以腾出时间来提高性能,同时仍然可以按时交付我的软件。
用C#编写良好的UI比使用C ++容易,因此我更有可能在UI保持响应的同时将工作推送到后台,或者在程序不得不阻塞一会儿时提供进度或听到的UI。这不会使任何事情变得更快,但是会使用户对等待更加满意。
我说的关于C#的一切可能对Java都是正确的,只是我没有经验可以肯定地说。
如果您是一位学习C ++的Java / C#程序员,那么您会很想继续使用Java / C#进行思考,并将逐字转换为C ++语法。在这种情况下,您只会得到前面提到的本机代码与解释型/ JIT相比的好处。为了在C ++与Java / C#中获得最大的性能提升,您必须学习在C ++中进行思考,并专门设计代码以利用C ++的优势。
释义Edsger Dijkstra:[您的第一语言]残缺了无法康复的思想。
用杰夫·阿特伍德(Jeff Atwood)来解释:您可以用任何新语言写[您的第一语言]。
虚拟机语言不太可能胜过编译语言,但它们可以足够接近而无所谓,至少出于以下原因(由于从未使用过C#,我在这里使用Java是因为)。
1 / Java运行时环境通常能够检测经常运行的代码段,并对这些部分执行即时(JIT)编译,以便将来以完整的编译速度运行。
2 / Java库的大部分已编译,因此,当您调用库函数时,您正在执行的是编译后的代码,而不是解释代码。您可以通过下载OpenJDK来查看代码(用C语言编写)。
3 /除非您要进行大量计算,否则程序在大多数时间都在运行,它会等待非常慢的(相对而言)人的输入。
4 /由于在加载类时对Java字节码进行了很多验证,因此大大减少了运行时检查的常规开销。
5 /在最坏的情况下,可以将性能密集的代码提取到已编译的模块中,然后从Java中调用(请参阅JNI),以使其全速运行。
总而言之,Java字节码永远不会超过本地机器语言,但是有一些方法可以减轻这种情况。Java(如我所见)的最大优势是巨大的标准库和跨平台的特性。
Orion Adrian,让我倒转您的文章,看看您的言论有多无根据,因为关于C ++的说法也很多。并告诉Java / C#编译器优化空函数确实会让您听起来好像不是我的优化专家,因为a)为什么真正的程序应该包含空函数,除了非常糟糕的旧版代码之外,b)确实不是黑色和出血边缘优化。
除了那句话之外,您还公然地使用了指针,但是Java和C#中的对象基本上不像C ++指针那样工作吗?它们可以不重叠吗?他们可以不为空吗?C(以及大多数C ++实现)具有limit关键字,都具有值类型,C ++具有非空保证的引用值。Java和C#提供什么?
通常,C和C ++可以一样快或更快,因为AOT编译器(可以在部署之前一劳永逸地在许多核心构建服务器的高内存上编译您的代码的编译器)可以对C#编译程序进行优化不能,因为有很多时间可以这样做。编译器可以确定该计算机是Intel还是AMD;否则,请执行以下操作。奔腾4,Core Solo或Core Duo;或者是否支持SSE4等,并且如果您的编译器不支持运行时调度,则可以通过部署一些专用二进制文件自己解决问题。
AC#程序通常是在运行时进行编译的,因此它可以在所有机器上正常运行,但是没有针对单个配置(即处理器,指令集,其他硬件)进行尽可能多的优化,并且必须花费一些时间第一。诸如循环裂变,循环反转,自动矢量化,整个程序优化,模板扩展,IPO等功能非常难以完全解决,而不会干扰最终用户。
此外,某些语言功能允许C ++或C编译器对您的代码进行假设,从而使它们可以优化某些部分,而这对于Java / C#编译器来说是不安全的。当您无法访问泛型的完整类型ID或有保证的程序流时,有很多优化是不安全的。
同样,C ++和C只需一次增加寄存器就可以一次完成许多堆栈分配,这肯定比Javas和C#分配在垃圾收集器和您的代码之间的抽象层上效率更高。
现在,我不能再说Java了,但是我知道,例如C ++编译器在知道方法主体为空时实际上将删除方法和方法调用,它将消除常见的子表达式,可以尝试重试为了找到最佳的寄存器用法,它不执行边界检查,它将对循环和内部循环进行自动向量化,并且将内到外反转,将条件移出循环,将循环拆分和不拆分。它将按照C方式将std :: vector扩展为本机零开销数组。它将进行程序间优化。它将直接在调用者站点上构造返回值。它将折叠并传播表达式。它将以缓存友好的方式将数据重新排序。它将执行跳转线程。它使您可以以零的运行时间开销编写编译时射线跟踪器。它将进行非常昂贵的基于图的优化。如果它将某些代码替换为语法上完全不相等但在语义上等效的代码,则它将降低强度(旧的“ xor foo,foo”只是最简单的,尽管此类过时的优化)。如果您有疑问,可以省略IEEE浮点标准,并启用更多的优化功能,例如浮点操作数重新排序。在对代码进行按摩和破坏之后,它可能会重复整个过程,因为通常,某些优化为甚至某些优化奠定了基础。它也可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在您的代码中使用这种逻辑。是否用语法上完全不相等但语义上等效的代码替换了某些代码(旧的“ xor foo,foo”只是最简单的,但这种类型的过时优化)。如果您有疑问,可以省略IEEE浮点标准,并启用更多的优化功能,例如浮点操作数重新排序。在对代码进行按摩和破坏之后,它可能会重复整个过程,因为通常,某些优化为甚至某些优化奠定了基础。它也可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在您的代码中使用这种逻辑。是否用语法上完全不相等但语义上等效的代码替换了某些代码(旧的“ xor foo,foo”只是最简单的,但这种类型的过时优化)。如果您有疑问,可以省略IEEE浮点标准,并启用更多的优化功能,例如浮点操作数重新排序。在对代码进行按摩和破坏之后,它可能会重复整个过程,因为通常,某些优化为甚至某些优化奠定了基础。它也可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在您的代码中使用这种逻辑。如果您有疑问,可以省略IEEE浮点标准,并启用更多的优化功能,例如浮点操作数重新排序。在对代码进行按摩和破坏之后,它可能会重复整个过程,因为通常,某些优化为甚至某些优化奠定了基础。它也可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在您的代码中使用这种逻辑。如果您有疑问,可以省略IEEE浮点标准,并启用更多的优化功能,例如浮点操作数重新排序。在对代码进行按摩和破坏之后,它可能会重复整个过程,因为通常,某些优化为甚至某些优化奠定了基础。它还可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在整个代码中使用这种逻辑。它也可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在您的代码中使用这种逻辑。它还可能只是使用改组后的参数重试,并查看其他变体在其内部排名中的得分。并且它将在整个代码中使用这种逻辑。
因此,正如您所看到的,某些C ++或C实现会更快的原因很多。
综上所述,可以使用C ++进行许多优化,这些优化将使您无法使用C#进行的所有工作,特别是在数字运算,实时和接近金属领域,但不仅限于此。您甚至不必触摸单个指针就可以走很长一段路。
因此,根据您所写的内容,我会选择其中之一。但是,如果您编写的内容与硬件无关(驱动程序,视频游戏等),那么我就不必担心C#的性能(再也不能说Java)。会很好的。
通常,某些通用论点在特定帖子中听起来很酷,但通常听起来并不可信。
无论如何,要和平:AOT和JIT都很棒。唯一正确的答案可以是:这取决于。真正的聪明人知道,无论如何,您都可以利用两全其美的方法。
如果Java解释器生产,实际上是机器代码,它只会发生更好的比机器代码的编译器生成你正在写的C ++代码的优化,到所在的C ++代码比Java和解释成本慢点。
但是,实际上发生这种情况的几率很低-除非Java拥有一个编写得很好的库,并且您有自己编写得不好的C ++库。
实际上,C#并不像Java那样在虚拟机中真正运行。IL被编译为汇编语言,该语言完全是本机代码,并且以与本机代码相同的速度运行。您可以对.NET应用程序进行预JIT,这将完全消除JIT成本,然后您将运行完全本机代码。
.NET变慢的原因不是因为.NET代码更慢,而是因为它在后台执行了很多工作,如垃圾收集,检查引用,存储完整的堆栈框架等。这在执行时会非常强大且有用建立应用程序,但也要付出代价。请注意,您也可以在C ++程序中完成所有这些操作(许多核心.NET功能实际上是.NET代码,您可以在ROTOR中查看)。但是,如果您手动编写相同的功能,由于.NET运行时已经过优化和微调,因此最终程序可能会慢得多。
就是说,托管代码的优势之一是它可以完全验证。您可以在执行代码之前验证该代码永远不会访问其他进程的内存或对它们进行不明智的操作。微软拥有一个完全托管操作系统的研究原型,令人惊讶地表明,利用此验证功能来关闭托管程序不再需要的安全功能,一个100%托管环境实际上可以比任何现代操作系统更快地运行。 (在某些情况下,我们说的是10倍)。SE电台在谈论这个项目时有一段精彩的插曲。
在某些情况下,托管代码实际上可以比本机代码更快。例如,“标记清除”垃圾收集算法允许JRE或CLR之类的环境在一次通过中释放大量短期(通常)的对象,其中大多数C / C ++堆对象是一次释放的。一次。
来自维基百科:
出于许多实际目的,用垃圾回收语言实现的分配/取消分配密集型算法实际上比使用手动堆分配的等效算法更快。这样做的主要原因是,垃圾收集器允许运行时系统以潜在的有利方式摊销分配和释放操作。
也就是说,我编写了很多C#和很多C ++,并且运行了很多基准测试。以我的经验,C ++在两种方面比C#快得多:(1)如果您使用用C#编写的某些代码,请将其移植到C ++,本机代码往往会更快。快多少?嗯,它变化很大,但是速度提高100%并不少见。(2)在某些情况下,垃圾回收会大大降低托管应用程序的速度。.NET CLR的大堆(例如,> 2GB)做得很糟糕,并且最终可能会花费大量时间在GC上,甚至在具有很少甚至没有中间寿命对象的应用程序中也是如此。
当然,在我遇到的大多数情况下,托管语言足够快,而且远见卓识,而为了获得C ++的额外性能而在维护和编码方面的权衡根本不是一个好方法。
这是一个有趣的基准 http://zi.fi/shootout/
阅读有关HP Labs的Dynamo的信息,Dynamo是PA-8000的解释程序,它可以在PA-8000上运行,并且运行程序的速度通常比本地程序快。那就一点也不奇怪了!
不要将其视为“中间步骤”-运行程序已经涉及许多其他步骤,无论使用哪种语言。
通常可以归结为:
程序具有热点,因此,即使您在运行95%的代码主体时运行得较慢,但在5%的情况下运行得更快,仍然可以在性能上保持竞争力
HLL比C / C ++等LLL更了解您的意图,因此可以生成更优化的代码(OCaml甚至更多,并且在实践中通常更快)
JIT编译器具有很多静态编译器没有的信息(例如,您这次恰好拥有的实际数据)
JIT编译器可以在运行时进行传统链接器实际上不允许执行的优化(例如对分支进行重新排序,以使常见情况变得平缓,或者内联库调用)
总而言之,C / C ++的性能很差劲:关于您的数据类型的信息很少,没有关于数据的信息,也没有动态运行时,从而无法进行很多运行时优化。
我的理解是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极力支持的答案的某些问题(因为有人不断删除我对他的答案的评论),这种观点产生了适得其反的两极化观点:
代码处理将在编译时完成...
因此,模板元编程仅在程序在编译时可用的情况下才有效(通常不是这种情况),例如,不可能在香草C ++中编写具有竞争力的正则表达式库,因为它无法运行时代码生成(元编程)。
...使用类型进行播放是在编译时完成的... Java或C#中的等效项充其量很难编写,并且即使在编译时知道类型,也总是较慢并在运行时解析。
在C#中,这仅适用于引用类型,而不适用于值类型。
不管JIT优化如何,直接指针访问内存都不会很快...如果您在内存中有连续数据,则通过C ++指针(即C指针...让Caesar承担应有的访问权限)访问它的速度会快上很多倍。比Java / C#中的要好。
人们已经观察到Java在SciMark2基准测试的SOR测试中击败了C ++,正是因为指针阻碍了与别名相关的优化。
同样值得注意的是,.NET在链接后确实在动态链接的库之间进行泛型的类型特殊化,而C ++则不能,因为必须在链接之前解析模板。显然,泛型相对于模板的最大优势是可理解的错误消息。
除了其他人所说的以外,据我了解,.NET和Java在内存分配方面更胜一筹。例如,它们可以压缩内存,因为内存会变得碎片化,而C ++则不能(本机,但如果您使用的是聪明的垃圾收集器,则可以)。
对于需要大量速度的任何事物,JVM只是调用C ++实现,因此,与大多数操作系统相关的事情相比,JVM的性能要好得多是一个问题。垃圾回收会将您的内存减少一半,但是使用一些更高级的STL和Boost功能将具有相同的效果,但潜在的错误几倍。
如果您仅在具有许多类的大型项目中使用C ++库及其许多高级功能,则可能会比使用JVM慢。除了更容易出错。
但是,C ++的好处是它允许您优化自己,否则您将被编译器/ jvm所困扰。如果您创建自己的容器,编写自己的对齐内存管理,使用SIMD,然后在此处进行组装,则可以比大多数C ++编译器自行完成的速度提高至少2到4倍。对于某些操作,为16x-32x。那就是使用相同的算法,如果您使用更好的算法并进行并行化,则增长可能会非常惊人,有时会比常用方法快数千倍。
我从几个不同的角度来看它。