托管C ++与UnManaged / native C ++的性能

时间:2010-06-10 16:39:29

标签: c++ performance unmanaged managed

我正在编写一个非常高性能的应用程序,每毫秒处理和处理数百个事件。

非托管C ++是否比托管c ++更快?为什么?

托管C ++处理CLR而不是OS和CLR负责内存管理,这简化了代码,并且可能比非托管C ++中“程序员”编写的代码更有效?还是有其他原因? 使用托管时,如果对程序员透明并由CLR处理,那么如何避免动态内存分配(这会导致性能损失)?

回到我的问题,托管C ++在速度方面是否比非托管C ++更有效?为什么?

9 个答案:

答案 0 :(得分:8)

对此没有一个答案。作为一个真正的通用规则,本机代码通常会更快,但1)情况并非总是如此,2)有时差异太小而无法关注,以及3)代码写得多好通常会比管理与非管理产生更大的差异。

托管代码在虚拟机中运行。基本上,您从一个生成字节代码作为输出的编译器开始,然后将其提供给虚拟机。然后,虚拟机将其重新编译为机器代码并执行该代码。在某些情况下,这可以提供一些真正的优势。举一个例子,如果你有一个运行64位VM的64位处理器(差不多已经给定了)但是在64位处理器之前编写的旧程序很常见,VM仍然会将该字节代码编译为64-位机代码,它可以为至少一些代码提供相当大的速度优势。

同时,对于某些代码来说,它也可能是一个相当明显的缺点。特别是,编译器在用户等待时运行。为了适应这种情况,VM的编译器本身运行速度非常慢。虽然本机代码生成器有所不同,但是您选择的本机编译器至少包含一些在VM的字节码编译器中放弃的优化以保证其资源使用合理的可能性很大。

VM也使用垃圾收集器。垃圾收集器与手动管理内存具有相当不同的特性。对于许多手动管理器,分配内存相当昂贵。释放内存相当便宜,但与您发布的项目数量大致呈线性关系。其他手动管理人员大致反过来说,在释放内存时做额外的工作以便更快地进行分配。无论哪种方式,成本结构都不同于典型的收集器。

使用垃圾收集器,分配内存通常非常便宜。对于典型的(复制)收集器,释放内存的成本主要取决于已分配且仍然(至少可能)正在使用的对象的数量。

分配本身也有所不同。在本机C ++中,您通常在堆栈上创建大多数对象,其中分配释放内存非常便宜。在托管代码中,您通常会动态分配更大百分比的内存,并在其中进行垃圾回收。

答案 1 :(得分:3)

您可以用任何语言编写慢速代码;相反,你可以使用体面的算法,这种算法几乎可以用于任何语言。

这里常见的答案是选择一种你已经知道的语言,使用适当的算法,然后对其进行分析以确定实际的热点。

我有点担心数百个事件每毫秒语句。这太可怕了。您是否合理地能够以任何语言进行预期的处理?

作为高性能系统上的C ++开发人员,我倾向于相信我能够分析和优化发出的代码。那说;有很高性能的.NET应用程序,编写器已经竭尽全力不在关键循环中进行动态内存分配 - 主要是通过使用预先创建的已分配的对象池。

所以重复我以前的评论:选择你已经知道的,然后调整。即使你走到了尽头;你可能会对你的问题空间有更多了解。

答案 2 :(得分:2)

这一切都取决于具体情况。

使非托管代码更快/托管代码更慢的事情:

  • 代码需要在执行之前转换为机器代码
  • 垃圾收集可能会导致开销
  • 从托管代码到非托管代码的调用会产生严重的开销
  • 非托管编译器可以优化更多,因为它们直接生成机器代码(见过我自己)

使托管代码更快/非托管代码更慢的事情:

  • 由于代码在使用之前就被转换为机器代码,因此可以针对实际处理器优化托管代码(使用非托管代码,您必须将目标定位为“最低支持”处理器)。

可能还有更多原因。

答案 3 :(得分:2)

托管代码在大多数情况下比非托管代码慢,即使.Net CLR在执行代码之前总是进行JIT编译(在程序运行时它没有多次编译但是它从不解释代码)

问题在于CLR做了很多检查,例如每当您尝试访问它时,查看是否在数组的边界上运行。这样可以减少缓冲区溢出等问题,但也会因为这些检查增加的开销而导致性能下降。

我已经看过C#优于C ++的实验,但这些实验是在代码中充分利用对象层次结构等进行的。当涉及数字运算时,你想要充分利用你的PC,你将不得不去使用非托管代码。

另外一点也已经提到了 - 当必须释放内存时,GC导致程序执行中有些不可预测的暂停。在非托管代码中进行内存管理时,您也需要这个时间,但是当您决定销毁一个对象时,它会更频繁地发生,这意味着它不会立即为整个程序完成,因此您不会有长时间的停顿。 / p>

答案 4 :(得分:0)

这里有很多好的答案,但是托管代码的一个方面可能是长期优势,它是运行时分析。由于托管编译器生成的代码是中间格式,因此可以根据实际使用情况优化实际执行的机器代码。如果大量使用特定的功能子集,JIT'er可以将机器代码本地化在同一个内存页面上,从而增加了局部性。如果从特定方法重复进行特定子调用,则JIT'er可以动态内联它。

这是对非托管代码的改进,其中内联必须提前“猜测”,并且过度内联是有害的,因为它会增加代码大小并导致导致(非常耗时)L2 / L1缓存未命中的位置问题。这些信息根本不适用于静态分析,因此只能在JIT环境中使用。运行时分析可以获得一系列可能的优势,例如优化的循环展开等。

我并不是说.NET JIT'er尽可能聪明,但我知道我已经听说过全局分析功能,我知道很多关于运行时分析的研究都是在Hewlett-Packard完成的和其他公司。

答案 5 :(得分:0)

写快速代码,总是很痛苦。您可以针对一个平台优化的主要问题。这在控制台,嵌入式或其他平台上确实是一种情况,其中硬件始终是相同的。在真实的PC世界中,情况并非如此。不同的核心,不同的建设环境......让这成为一场噩梦。这是主要的问题,imho,真正区分man / unam代码。人。代码可以在运行时对新平台进行乐观优化。 Unman代码没有写入石头。

答案 6 :(得分:-1)

首先,你的陈述“每毫秒处理数百个事件。”听起来很不切实际。除非您在计算机中有一个特殊设计的时钟模块,否则我不认为您可以通过通用PC实现目标(典型分辨率大约为10毫秒)。其次,Native C ++在性能方面要好得多。在C ++方面可以采取很多优化来加速,而在托管代码中它们是不可能的。还要注意托管代码中的垃圾收集会使性能无法预测 - 当GC启动时,整个过程会被冻结。一旦遇到问题,解决方案就会更加痛苦,现在托管代码提供的所有“好样式”都将消失。

至于管理代码可以针对CPU优化的能力,这是正确的,但您也可以利用本机C ++中的CPU功能(SSE2,MMX等)。根据我的经验,性能提升可以忽略不计。

答案 7 :(得分:-1)

按速度和电源顺序排列>> C> C ++> = C ++ / CLI> C#> =所有其他。但是在asm中创建Web服务是一个长期的痛苦。然后在正确的时间使用正确的语言进行正确的工作和正确的听众,以做到最好。

答案 8 :(得分:-2)

C ++ / CLI不是像Java一样的半解释语言吗?

另外,昨天没有人发表研究表明GC系统总是比非GC慢?