C#真的比说C ++慢吗?

我一直在想这个问题一段时间了。

当然,C#中的某些东西并没有针对速度进行优化,所以使用这些对象或者语言调整(比如LinQ)可能会导致代码变慢。

但是,如果你不使用任何这些调整,只是比较C#和C ++中的相同的代码片段(很容易翻译到另一个)。 这真的会慢很多吗?

我看过一些比较表明C#在某些情况下可能会更快,因为理论上JIT编译器应该实时优化代码并获得更好的结果:

托pipe还是非托pipe?

我们应该记住,JIT编译器会实时编译代码,但是这是一次性开销,相同的代码(一旦到达和编译)不需要在运行时再次编译。

GC不会增加大量的开销,除非您创build并销毁数千个对象(如使用String而不是StringBuilder)。 而在C ++中这样做也将是昂贵的。

我想提出的另一点是在.Net中引入的DLL之间更好的通信。 .Net平台的通信要比托pipe的基于COM的DLL好得多。

我没有看到为什么语言应该慢一些的固有原因,我真的不认为C#比C ++慢(从经验和缺乏一个很好的解释)。

那么,用C#编写的相同代码会比C ++中的相同代码慢吗?
如果是的话,为什么?

其他的一些参考资料(其中有一点谈到,但没有解释为什么):

为什么你要使用C#,如果它比C ++慢?

警告:您提出的问题确实非常复杂 – 可能比您意识到的要多得多。 因此,这是一个非常长的答案。

从纯粹的理论观点来看,可能有一个简单的答案:C#没有(可能)没有真正阻止它像C ++一样快。 但是,尽pipe有这样的理论,但在某些情况下,有些事情会比较慢,这是有实际的原因的。

我将考虑三个基本的区别:语言function,虚拟机执行和垃圾收集。 后两者经常走在一起,但可以独立,所以我会分开看他们。

语言function

C ++非常重视模板,模板系统中的特性主要是为了尽可能在编译时完成,所以从程序的angular度来看,它们是“静态的”。 模板元编程允许在编译时进行完全的任意计算(即,模板系统是图灵完备的)。 因此,基本上不依赖于用户input的任何东西都可以在编译时计算出来,所以在运行时它只是一个常量。 然而,对此的input可以包含types信息之类的东西,所以通过C#中的运行时reflection,您通常会在编译时通过C ++中的模板元编程完成大量的工作。 尽pipe运行速度和多function之间肯定有一个折衷,模板可以做什么,它们是静态的,但是它们根本无法做任何reflection。

语言特征的差异意味着,几乎任何仅仅通过将C#转译成C ++(反之亦然)来比较两种语言的尝试都可能产生无意义和误导之间的结果(对于大多数其他语言来说也是如此以及)。 简单的事实是,对于任何大于几行代码的东西,几乎没有人可能以相同的方式使用这些语言(或者以相同的方式足够接近),这样的比较告诉你有关这些语言在现实生活中工作。

虚拟机

像几乎任何合理的现代虚拟机,微软的.NET可以并将做JIT(又名“dynamic”)编译。 这代表了一些权衡。

首先,优化代码(像大多数其他优化问题一样)在很大程度上是一个NP完全问题。 除了一个真正微不足道的/玩具程序之外,你几乎可以保证你不会真正地“优化”结果(也就是说,你不会find真正的最佳) – 优化器只会使代码更好以前。 然而,众所周知的一些优化需要花费大量的时间(通常是内存)来执行。 使用JIT编译器,用户正在等待编译器运行。 大多数更昂贵的优化技术被排除在外。 静态编译有两个优点:首先,如果速度很慢(例如,构build一个大系统),它通常在服务器上执行,而没有人花时间等待它。 其次,一个可执行文件可以被生成一次 ,并被很多人使用。 第一个最小化优化成本; 第二个分摊更大的执行数量小得多的成本。

正如原始问题(以及许多其他网站)中所提到的,JIT汇编确实有可能更好地了解目标环境,这应该(至less在理论上)抵消这个优势。 毫无疑问,这个因素至less可以抵消部分静态编译的缺点。 对于一些特定types的代码和目标环境,它甚至可以超过静态编译的优势,有时甚至是相当戏剧性的。 至less在我的testing和经验,但是,这是相当不寻常的。 依赖于目标的优化大多数似乎要么做出相当小的差异,要么只能(自动地,无论如何)应用于相当具体的问题types。 如果你在一台现代化的机器上运行一个相对较旧的程序,这种情况将会发生。 用C ++编写的旧程序可能已经被编译为32位代码,并且即使在现代的64位处理器上也将继续使用32位代码。 用C#编写的程序将被编译成字节码,然后虚拟机将编译成64位的机器码。 如果这个程序从64位代码运行中获得了实质性的好处,那么这会带来很大的好处。 在64位处理器相当新的一段时间,这发生了相当的数量。 最近可能受益于64位处理器的代码通常可以静态编译到64位代码中。

使用虚拟机还有可能提高caching的使用率。 虚拟机的指令通常比本地机器指令更紧凑。 他们中的更多人可以适应一定数量的caching内存,所以在需要的时候,您可以将任何给定的代码放在caching中。 这可以帮助保持VM代码的解释执行比大多数人最初期待的更具竞争力(在速度方面) – 在一个caching未命中的时间内,您可以在现代CPU上执行大量指令。

还值得一提的是,这两个因素并不一定是完全不同的。 没有什么能够阻止(例如)C ++编译器生成打算在虚拟机上运行的输出(带或不带JIT)。 事实上,微软的C ++ / CLI 几乎是一个(几乎)符合C ++的编译器(虽然有很多扩展),它们产生了在虚拟机上运行的输出。

反过来也是如此:微软现在拥有.NET Native,它将C#(或VB.NET)代码编译为本机可执行文件。 这样的performance通常更像C ++,但保留了C#/ VB的特性(例如,编译为本地代码的C#仍然支持reflection)。 如果你有性能密集的C#代码,这可能会有所帮助。

垃圾收集

从我所看到的,我认为垃圾收集是这三个因素中最贫穷的一个。 只是一个明显的例子,这里的问题提到:“GC不会增加很多的开销,除非你创build和销毁数千个对象[…]”。 实际上,如果你创build销毁数以千计的对象,垃圾收集的开销通常会相当低。 .NET使用了一代清道夫,这是一种复制收集器。 垃圾收集器通过从“地方”(例如,寄存器和执行堆栈)开始工作,指针/引用被认为是可访问的。 然后它“追逐”那些已经分配到堆上的对象的指针。 它检查这些对象是否有进一步的指针/引用,直到它将所有对象都追踪到任何链的末尾,并find所有(至less潜在地)可访问的对象。 在下一步中,它将使用所有(或至less可能正在使用的)对象,并通过将堆中的所有对象复制到堆中pipe理的内存的一端的连续块中来压缩堆。 其余的内存是免费的(模块化终结器必须运行,但至less在编写良好的代码中,它们非常罕见,以至于我暂时忽略它们)。

这意味着如果你创build和销毁大量的对象,垃圾收集会增加很less的开销。 垃圾收集周期所花费的时间几乎完全取决于已创build但未销毁的对象的数量。 快速创build和销毁对象的主要后果就是GC必须更频繁地运行,但每个周期仍然会很快。 如果创build对象并且销毁它们,那么GC将更频繁地运行, 并且每个周期都会大大减慢,因为它花费更多的时间来追踪指向潜在活动对象的指针, 并且花费更多时间复制仍在使用中的对象。

为了解决这个问题,世代清除工作的前提是,那些一直保持“活着”的对象可能会持续很长一段时间。 在此基础上,它有一个系统,在这个系统中,在一定数量的垃圾收集周期中存活的对象得到“终身”,垃圾收集器开始简单地假定它们仍在使用中,所以不是每个周期都复制它,他们一个人。 这是一个有效的假设,经常足以代清除典型的大多数其他forms的气相色谱的开销。

对“手动”内存pipe理的理解往往不甚理解。 举一个例子,许多比较的尝试都假定所有的手动内存pipe理都遵循一​​个特定的模型(例如,最适合的分配)。 与许多人关于垃圾收集的信念(例如,通常使用引用计数进行的广泛假设)相比,这往往很less(如果有的话)更接近现实。

鉴于垃圾收集手动内存pipe理的多种策略,在总体速度方面比较两者是相当困难的。 尝试比较分配和/或释放内存(本身)的速度几乎可以保证产生最好没有意义的结果,而最坏的情况是彻底的误导。

奖金主题:基准

由于不less博客,网站,杂志文章等都声称为这个或那个方向提供“客观”的证据,所以我也会在这个问题上投入我的两分钱。

这些基准中的大部分有点像青less年决定比赛他们的车,谁赢谁得到保持两辆车。 然而,网站却有一个重要的不同点:那就是发布基准testing的人会驾驶两辆车。 有一个奇怪的机会,他的赛车总是赢,而其他人都必须要“相信我,我真的是把车开得快了”。

编写一个糟糕的基准很容易,这个基准产生的结果几乎无所谓。 几乎任何人都可以在任何地方接近devise一个能够产生任何有意义的基准的技能,同样也有能力生产出他想要的结果的技能。 事实上,编写代码来产生一个特定的结果比编写真正产生有意义的结果更容易

正如我的朋友詹姆斯·坎泽(James Kanze)所说的那样,“永远不要相信一个基准你不会伪造你自己”。

结论

没有简单的答案。 我可以肯定的是,我可以掷硬币来select赢家,然后从1到20之间挑选一个数字来表示赢得的比例,然后写出一些看似合理和公平的基准的代码, (至less在一些目标处理器 – 一个不同的处理器可能会改变一点点)。

正如其他人所指出的,对于大多数代码来说,速度几乎是无关紧要的。 对此的必然结果(常常被忽略)是在速度很重要的小代码中,它通常很重要。 至less在我的经验中,对于代码真正重要的代码来说,C ++几乎总是胜利者。 肯定有一些支持C#的因素,但实际上它们似乎被C ++所偏爱的因素所压倒。 你当然可以find基准来表明你select的结果,但是当你编写真正的代码时,你几乎总是可以在C ++中比在C#中更快。 它可能(或可能不)需要更多的技巧和/或努力来写,但实际上总是可能的。

因为你并不总是需要使用(而我使用这个松散的)“最快”的语言? 只是因为速度更快,我不会开车去法拉利工作。

C ++总是有性能优势。 使用C#,我无法处理内存,并且我有大量的资源可用于执行我的工作。

你需要质疑自己更多的是关于哪一个节省你的时间。 机器现在非常强大,你的代码大部分都应该用一种语言来完成,这样可以让你在最短的时间内获得最大的价值。

如果在C#中有一个核心处理过程耗时过长,则可以使用C#构build一个C ++并与之互操作。

停止考虑你的代码性能。 开始build立价值。

大约2005年,来自本土/pipe理围栏两边的两名MSperformance专家试图回答同样的问题。 他们的方法和过程仍然令人着迷,今天的结论仍然存在 – 我不知道有什么更好的尝试给出明智的答案。 他们指出,关于绩效差异的潜在原因的讨论是假设性的和徒劳的,真正的讨论必须有一些经验基础来解决这种差异的真实世界影响。

所以, 新老雷蒙德和马里亚尼为友谊赛制定了规则。 一个中文/英文字典被选为一个玩具应用上下文:足够简单,被编码为一个业余爱好项目,但足够复杂,以演示不平凡的数据使用模式。 规则开始很简单–Raymond编写了一个简单的C ++实现,Rico 逐行将其迁移到C#,没有任何复杂性,两个实现都运行了一个基准。 之后,随后进行了几次优化。

全部细节在这里: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 。

这个泰坦巨人的对话是非常有教育意义的,我衷心推荐潜入 – 但是如果你缺乏时间和耐心,杰夫·阿特伍德(Jeff Atwood)

在这里输入图像说明

最终,C ++的速度提高了2倍,但最初速度却降低了13倍。

正如Rico 总结的那样 :

所以我为自己惨败而感到羞耻? 几乎不。 托pipe代码几乎没有任何努力取得了很好的结果。 为了击败pipe理版本,雷蒙德不得不:

  • 写他自己的文件/ io的东西

  • 写他自己的string类

  • 写他自己的分配器

  • 写他自己的国际映射

当然,他使用可用的低级库来做到这一点,但这仍然是一个很大的工作。 你可以调用什么是一个STL程序? 我不这么认为。

那是我的经验,11年,谁知道C#/ C ++版本有多less。

当然,这不是巧合,因为这两种语言壮观地实现了完全不同的devise目标。 C#希望被用在开发成本是主要考虑因素(仍然是大多数软件)的地方,而C ++则可以帮助您节省大量的开发成本,包括游戏,algorithm交易,中心等

C#比C ++更快。 写得更快 对于执行时间,没有什么比击败一个分析器。

但是C#并没有像C ++那样可以轻松实现接口。

而C#严重依赖于Windows …

顺便说一下,时间关键的应用程序不是用C#或Java编码的,主要是由于垃圾收集何时执行的不确定性。

在现代,应用程序或执行速度并不像以前那么重要。 开发进度,正确性和健壮性是更高的优先级。 如果一个应用程序的高速版本存在大量的错误,崩溃了很多或者更糟糕,错过了一个上市或部署的机会,那么这个应用程序的高速版本并不好。

由于发展计划是重中之重,因此新的语言正在加速发展。 C#就是其中之一。 C#还通过从C ++中删除导致常见问题的特性来帮助正确性和健壮性:一个示例是指针。

使用C ++开发的应用程序和使用C ++开发的应用程序在执行速度上的差异在大多数平台上可以忽略不计。 这是由于执行瓶颈不依赖于语言,但通常取决于操作系统或I / O。 例如,如果C ++在5毫秒内执行一个函数,但C#使用2毫秒,并且等待数据需要2秒,则与等待数据的时间相比,在函数中花费的时间是微不足道的。

select最适合开发人员,平台和项目的语言。 努力实现正确,稳健和部署的目标。 应用程序的速度应视为一个错误:优先考虑它,与其他错误比较,并根据需要进行修复。

一个更好的方式来看待它的一切都比C / C ++慢,因为它抽象而不是遵循棒和泥的范例。 这就是所谓的系统编程的原因,你对谷物或裸机进行编程。 这样做也会使您无法使用C#或Java等其他语言来实现。 但是可悲的是,C语言的根本就是用艰辛的方式来做事情,所以你最主要的是编写更多的代码,花更多的时间去debugging它。

C也是区分大小写的,C ++中的对象也遵循严格的规则集。 例如,一个紫色的冰淇淋锥可能不是一个蓝色的冰淇淋锥,虽然他们可能是圆锥体,他们可能不一定属于锥体家庭,如果你忘了定义什么锥你错了。 因此,冰淇淋的特性可能是或可能不是克隆。 现在速度参数,C / C ++使用堆栈和堆方法,这是裸机获取它的金属。

随着助推库,你可以实现令人难以置信的速度不幸的是大多数游戏工作室坚持标准库。 另一个原因可能是因为用C / C ++编写的软件往往文件大小很大,因为它是一个巨大的文件集合,而不是单个文件。 还要注意所有的操作系​​统都是用C语言编写的,所以一般我们为什么要问这个问题可能会更快?

另外caching并不比纯内存pipe理快,对不起,但这只是不扇出来。 内存是物理的东西,caching是软件为了获得性能踢的东西。 人们也可以推断,没有物理内存caching就根本不存在。 这并不意味着内存必须在某种程度上被pipe理,无论是自动的还是手动的。

如果有更快的路由(C#),为什么要编写一个小的应用程序,它不需要太多的C ++优化?

只有在特定系统上执行基准testing,才能得到确切的答案。 然而,思考C#和C ++等编程语言之间的一些根本区别仍然很有趣。

汇编

执行C#代码需要额外的步骤代码被JIT。 关于将有利于C ++的性能。 另外,JIT编译器只能在被JIT处理的代码单元(例如方法)内优化生成的代码,而C ++编译器可以使用更激进的技术跨越方法调用进行优化。

但是,JIT编译器能够优化生成的机器代码,以紧密匹配底层硬件,使其能够利用其他硬件function。 据我所知,.NET JIT编译器并没有这样做,但它可以构想为Atom生成不同的代码,而不是Pentium CPU。

内存访问

垃圾收集架构在很多情况下可以创build比标准C ++代码更优化的内存访问模式。 如果用于第一代的内存区域足够小,则可以保持在CPUcaching内增加性能。 如果您创build并销毁大量小对象,则维护托pipe堆的开销可能会小于C ++运行时所需的开销。 再一次,这是高度依赖于应用程序。 性能的Python研究表明,由于更优化的内存访问模式,特定的托pipePython应用程序能够比编译版本更好地扩展。

主要担心的不是速度,而是Windows版本和升级的稳定性。 Win32在Windows版本中几乎是免疫的,因此非常稳定。

当服务器退役和软件迁移的时候,使用.Net的任何东西都会产生焦虑,通常很多.net版本都会出现恐慌,但是10年前build立的Win32应用程序却一直在运行。

不要混淆!

  • 如果C#应用程序是最好的情况下编写的,而C ++应用程序是最好的情况下编写的,那么C ++更快。
    这里有很多原因是为什么C ++本身就是C#更快的,比如C#使用类似于Java中的JVM的虚拟机。 基本上高级语言的性能较差(如果使用最好的情况)。

  • 如果您是一位经验丰富的专业C#程序员,就像您是经验丰富的专业C ++程序员一样,使用C#开发应用程序比C ++更简单快捷。

这些情况之间的许多其他情况是可能的。 例如,您可以编写C#应用程序和C ++应用程序,以便C#应用程序比C ++应用程序运行得更快。

对于select一种语言,你应该注意项目的情况和主题。 对于一般的商业项目,你应该使用C#。 对于像Video Converter或Image Processing项目这样的高性能项目,您应该selectC ++。

更新:

好。 让我们来比较一下为什么C ++的最可能的速度超过C#的一些实际原因。 考虑一个好的C#应用​​程序和相同的C ++版本:

  • C#使用虚拟机作为中间层来执行应用程序。 它有开销。
  • AFAIK CLR无法优化目标机器中的所有C#代码。 C ++应用程序可以在最优化的目标机器上编译。
  • 在C#中,运行时最可能的优化意味着最可能的快速虚拟机。 虚拟机已经开销了。
  • C#是更高级的语言,因此它为最终进程生成更多的程序代码行。 (考虑一下Assembly程序和Ruby之间的区别,C ++和更高级的语言(如C#/ Java)之间的相同条件)

如果你喜欢在实践中获得更多的信息作为专家, 看看这个 。 这是关于Java,但也适用于C#。

我一直专注于优化大约15年,并定期重新编写C ++代码,尽可能多地使用编译器内部函数,因为C ++性能往往远不及CPU的能力。 caching性能经常需要考虑。 许多向量math指令都需要replace标准的C ++浮点代码。 大量的STL代码被重写,并且经常以更快的速度运行。 随着CPU接近其最佳性能,数据和代码大量使用数据,可以写出惊人的结果。

这在C#中是不可能的。 比较他们的相对#真正的时间#performance真的是一个惊人的无知的问题。 C ++中速度最快的代码将在每个汇编指令针对手头任务进行优化时完成,而不需要任何指令。 在需要时使用每一块内存,而不是复制n次,因为这是语言devise所要求的。 每个需要的内存移动都与caching协调一致。 如果最终的algorithm无法得到改进,那么就要根据精确的实时要求,考虑精度和function。

那么你将接近最佳的解决scheme。

比较C#与这个理想的情况是惊人的。 C#无法竞争。 实际上,我现在正在重新编写一大堆C#代码(当我说重新编写时,我的意思是删除并完全replace它),因为它甚至不在同一个城市,更不用说公园了。性能。

所以,请不要自欺欺人。 C#很慢。 死缓。 所有的软件都在减速,C#正在使这个速度下降。 所有软件都使用汇编程序中的读取执行周期(您知道 – 在CPU上)运行。 你使用10倍的指令; 它会慢10倍。 你瘫痪了caching; 它会变得更慢。 你添加垃圾收集到一个实时的软件,然后你经常被愚弄,认为代码运行“好”,只是有些时候,当代码“有点慢”的时候。

尝试添加一个垃圾收集系统来编码每个周期的计数。 我不知道股市交易软件是否有垃圾收集(你知道 – 在新的海底电缆上运行,耗资3亿美元?)。 我们可以每2秒钟备用300毫秒吗? 怎么样的航天飞机上的飞行控制软件 – GC在那里呢? 性能车辆上的发动机pipe理软件如何? (一个赛季的胜利可能值得数百万美元)。

垃圾收集是一个彻底的失败。

所以不,重点是C ++要快得多。 C#是一个倒退。