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

Yoc*_*mer 74 c# c++ performance

我一直想知道这个问题一段时间了.

当然,C#中有些东西没有针对速度进行优化,因此使用这些对象或语言调整(如LinQ)可能会导致代码变慢.

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

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

管理还是不管理?

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

除非你创建和销毁数以千计的对象(比如使用String而不是StringBuilder),否则GC不会增加很多开销.用C++做这件事也会很昂贵.

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

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

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

其他一些参考文献(关于这一点,但没有解释为什么):

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

Jer*_*fin 140

警告:你提出的问题非常复杂 - 可能比你意识到的要多得多.因此,这是一个非常长的答案.

从纯粹的理论角度来看,可能有一个简单的答案:(可能)没有任何关于C#的东西真正阻止它像C++一样快.然而,尽管有这个理论,但有一些实际的原因在某些情况下某些事情变慢.

我将考虑三个基本的差异区域:语言功能,虚拟机执行和垃圾收集.后两者经常在一起,但可以是独立的,所以我会分别看一下.

语言特色

C++非常强调模板和模板系统中的功能,这些功能主要是为了尽可能地在编译时完成,因此从程序的角度来看,它们是"静态的".模板元编程允许在编译时执行完全任意的计算(即,模板系统是图灵完成).因此,基本上任何不依赖于用户输入的内容都可以在编译时计算,因此在运行时它只是一个常量.但是,对此的输入可以包括类型信息之类的内容,因此在C#中通过运行时反射执行的大量操作通常在编译时通过C++中的模板元编程完成.在运行时速度和多功能性之间肯定存在权衡 - 模板可以做什么,它们是静态的,但它们根本无法做所有反射.

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

虚拟机

像几乎任何合理的现代虚拟机一样,微软的.NET可以并且将会进行JIT(又称"动态")编译.这代表了许多权衡.

首先,优化代码(像大多数其他优化问题一样)主要是NP完全问题.除了一个真正的琐碎/玩具程序,你几乎可以肯定你不会真正"优化"结果(即,你不会找到真正的最佳) - 优化器将简单地使代码比它更好以前是.然而,众所周知的相当多的优化需要花费大量时间(并且通常是内存)来执行.使用JIT编译器,用户在编译器运行时正在等待.大多数更昂贵的优化技术被排除在外.静态编译有两个优点:首先,如果它很慢(例如,构建一个大型系统),它通常在服务器上执行,没有人花时间等待它.其次,可执行文件可以生成一次,并且被许多人多次使用.第一个是最小化优化成本; 第二种方法可以在更大数量的执行中分摊更小的成本.

正如原始问题(以及许多其他网站)中所提到的,JIT编译确实有可能提高对目标环境的认识,目标环境应该(至少在理论上)抵消这种优势.毫无疑问,这个因素至少可以抵消静态编译的部分缺点.对于一些相当特定类型的代码和目标环境,它甚至可以超过静态编译的优势,有时相当显着.至少在我的测试和经验中,这是相当不寻常的.目标依赖优化大多似乎要么产生相当小的差异,要么只能(无论如何自动地)应用于相当类型的问题.很明显,如果你在现代机器上运行一个相对较旧的程序,就会发生这种情况.用C++编写的旧程序可能已编译为32位代码,即使在现代64位处理器上也会继续使用32位代码.用C#编写的程序将被编译为字节代码,然后VM将编译为64位机器代码.如果该程序从64位代码运行中获得了实质性的好处,那么这可以带来实质性的优势.在64位处理器相当新的时候,这种情况发生了很多.最近可能从64位处理器中受益的代码通常可以静态编译为64位代码.

使用VM还可以提高缓存使用率.VM的说明通常比本机机器指令更紧凑.它们中的更多可以适合给定数量的缓存,因此您可以更好地在需要时将任何给定代码放入缓存中.这可以帮助保持VM代码的解释执行比大多数人最初期望的更具竞争力(在速度方面) - 您可以在一次缓存未命中所花费的时间内在现代CPU上执行大量指令.

值得一提的是,这个因素在两者之间并不一定是不同的.没有什么能阻止(例如)C++编译器生成要在虚拟机上运行的输出(有或没有JIT).实际上,微软的C++/CLI 几乎就是 - 一个(几乎)符合C++的编译器(尽管有许多扩展),它产生的输出旨在在虚拟机上运行.

反过来也是如此:Microsoft现在拥有.NET Native,它将C#(或VB.NET)代码编译为本机可执行文件.这提供了通常更像C++的性能,但保留了C#/ VB的功能(例如,编译为本机代码的C#仍然支持反射).如果您有性能密集型的C#代码,这可能会有所帮助.

垃圾收集

从我所看到的,我会说垃圾收集是这三个因素中最贫穷的.仅举一个明显的例子,这里的问题提到:"GC也不会增加很多开销,除非你创建和销毁数千个对象[...]".实际上,如果您创建销毁数千个对象,垃圾收集的开销通常会相当低..NET使用代际清道夫,它是各种复制收集器.垃圾收集器通过从已知指针/引用可访问的"位置"(例如,寄存器和执行堆栈)开始工作.然后它"追逐"那些指向已在堆上分配的对象的指针.它会检查这些对象以获取进一步的指针/引用,直到它将所有对象跟踪到任何链的末尾,并找到所有(至少可能)可访问的对象.在接下来的步骤中,它需要所有的(或至少是对象的可能是在使用中),并通过复制它们全部成连续块在存储器中的一个端部在堆被管理压缩堆.然后其余的内存是免费的(必须运行模数终结器,但至少在编写良好的代码中,它们是非常罕见的,我暂时会忽略它们).

这意味着如果您创建并销毁大量对象,垃圾收集会增加很少的开销.由垃圾回收周期所花费的时间几乎完全依赖于已创建的,但对象的数量被破坏.在匆忙中创建和销毁对象的主要后果就是GC必须更频繁地运行,但每个周期仍然很快.如果你创建对象并没有摧毁他们,GC将更加经常运行每次循环的,因为它花费更多的时间追逐指针可能活物更慢,这它花费仍处于使用更多的时间复制对象.

为了解决这个问题,代清除工作,前提是该对象保持"活着"相当长一段时间可能会持续相当一段时间保持存活.在此基础上,它有一个系统,其中幸存下来的垃圾收集循环的一些数量的对象得到"终身",以及垃圾收集器开始简单地假设他们仍然在使用,所以不是每个周期复制它们,它只是叶子他们一个人.这通常是一个有效的假设,即代际清除通常比大多数其他形式的GC具有相当低的开销.

"手动"内存管理通常也很难理解.仅举一个例子,许多比较尝试都假设所有手动存储器管理也遵循一个特定模型(例如,最佳拟合分配).与许多人对垃圾收集的看法相比,这通常很少(如果有的话)更接近现实(例如,通常使用引用计数进行的普遍假设).

鉴于垃圾收集手动内存管理的策略多种多样,在整体速度方面比较两者非常困难.试图比较分配和/或释放内存的速度(单独使用)几乎可以保证产生最好无意义的结果,并且在最坏的情况下会产生彻底的误导.

奖金主题:基准

由于相当多的博客,网站,杂志文章等声称在一个方向或另一个方向提供"客观"证据,我也会在这个问题上投入2美分的价值.

大多数这些基准测试有点像青少年决定参加比赛,无论谁获胜都可以保留两辆车.不过,这些网站在一个关键方面有所不同:他们发布基准测试的人可以驾驶两辆车.通过一些奇怪的机会,他的车总能获胜,其他人都不得不满足于"相信我,我真的开得很快就开着你的车."

编写一个差的基准测试很容易产生几乎没有任何意义的结果.几乎任何人都能接近设计产生任何有意义的基准所必需的技能,也有能力制作一个可以给出他想要的结果的技能.事实上,编写代码来生成特定结果可能比实际产生有意义结果的代码更容易.

正如我的朋友詹姆斯坎泽所说的那样,"永远不要相信你没有伪造自己的基准."

结论

没有简单的答案.我有理由相信我可以翻一个硬币来选择获胜者,然后在(比方说)1到20之间选择一个数字来获得它所赢得的百分比,然后写一些看似合理公平的基准的代码,并且产生了预定的结论(至少在某些目标处理器上 - 不同的处理器可能会稍微改变一点).

正如其他人所指出的那样,对于大多数代码而言,速度几乎无关紧要.推论到(这是更经常被忽略)是在小代码中速度非常重要,它通常是相当重要的很多.至少根据我的经验,对于真正重要的代码,C++几乎总是胜利者.绝对有利于C#的因素,但在实践中,它们似乎被有利于C++的因素所抵消.您当然可以找到指示您选择结果的基准,但是当您编写实际代码时,您几乎总能在C++中使其比在C#中更快.它可能(或可能不会)花费更多的技巧和/或努力来编写,但它几乎总是可能的.

  • 在之前的公司,我们的一些决策是基于基准测试,但基准测试很难完成,因为它错过了更大的图景(例如对GC的影响)或者做得不好.注意基准测试...根据完成的情况做出决定是很诱人的.如果您做基准测试,那么尽量使其尽可能具有代表性:) (6认同)
  • IIRC,.NET运行时从不解释但总是JIT.优化由编译器在编译时(源代码到IL)和运行时抖动(IL到本机指令)完成.如果方法花费太长时间,jitter可能会选择不优化.如果需要完全优化,您可以使用NGen在安装时生成本机映像. (4认同)
  • 我认为答案实际上很简单:我的答案是,"是的,因为C#实际上迫使你创建数据结构引用类型,它为每一层包装增加了一个新的间接层,增加了内存流量并减少了内存局部性.在C++中,无论你在一个对象周围放置多少层包装,该对象都将保留在内存中的相同位置." 你同意吗? (2认同)
  • @Mehrdad:是的,不是.从实际的角度来看,是的,这可能会更有利于C++.同时,GC可以很容易地传递引用,以避免在许多情况下复制数据. (2认同)
  • @Mehrdad:是的 - 这就是我在思考我所说的内容:"你几乎总能让它在C++中比在C#中更快.它可能(或可能不会)需要更多的技巧和/或努力来写,但它几乎总是可能的." (2认同)
  • @Kr0e:不,“malloc”和“free”(通常)不是内核函数。通常,“malloc”偶尔会从操作系统分配一大块内存,调用“malloc”将给出其中的一部分。如果您一次分配“巨大”的内存块,那么它通常会被传递到操作系统。 (2认同)

Ada*_*rth 40

因为你并不总是需要使用(我使用这种松散的)"最快"的语言?我不开车在法拉利工作只是因为它更快......

  • 那么为什么*你开车去法拉利工作?来吧,不要让我们挂...... (23认同)
  • 我认为这个比喻很贴切.如果我驾驶法拉利去上班,我就不会再开车了.汽车当然能够比我驾驶的速度更快,但车辆的能力不是限制因素.有速度限制,交通,道路状况等.类似地,软件通常受用户交互,I/O等内容的约束.在这种情况下,您使用的语言不会有太大差异. (15认同)
  • 好吧,问题是"哪辆车是法拉利车,哪辆车是大众车?" 或两者都是法拉利. (3认同)

Max*_*ler 16

C++始终具有性能优势.使用C#,我无法处理内存,而且我可以使用大量资源来完成我的工作.

您需要提出的问题更多的是关于哪一个可以节省您的时间.机器现在非常强大,你的大多数代码都应该用一种语言来完成,这种语言可以让你在最短的时间内获得最大的价值.

如果在C#中存在一个耗时太长的核心处理,那么你可以构建一个C++并用C#互相转换.

不要再考虑代码性能了.开始建立价值.

  • +1 for*"不要考虑代码性能.开始建立价值."* (12认同)
  • 我同意C#的开发速度.在很多情况下它更有价值. (2认同)

Ofe*_*lon 15

大约2005年,来自原生/管理围栏双方的两名MS表现专家试图回答同样的问题.他们的方法和过程仍然令人着迷,今天的结论仍然存在 - 而且我不知道有任何更好的尝试给出明智的答案.他们指出,讨论绩效差异的潜在原因是假设和徒劳的,真正的讨论必须有一些经验基础来解决这些差异对现实世界的影响.

因此,Old New Raymond ChenRico Mariani为友谊赛制定了规则.选择中文/英文字典作为玩具应用程序上下文:简单到可以编码为业余爱好项目,但复杂到足以证明非平凡的数据使用模式.规则开始很简单--Raymond编写了一个简单的C++实现,Rico 逐行将其迁移到C#,没有任何复杂性,两个实现都运行了基准.之后,随后进行了几次优化迭代.

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

泰坦的这种对话非常有教育意义,我全心全意地建议潜入 - 但如果你缺乏时间或耐心,杰夫阿特伍德精心编制了底线:

在此输入图像描述

最终,C++的速度提高了2倍 - 但最初,它慢了13倍.

正如Rico 总结:

我的惨败让我感到羞耻吗?几乎不.托管代码几乎没有任何努力,取得了非常好的结果.为了打败托管版本,Raymond不得不:

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

  • 写自己的字符串类

  • 写自己的分配器

  • 写自己的国际地图

当然,他使用可用的低级库来做到这一点,但这仍然是很多工作.你能打电话给STL计划吗?我不这么认为.

这是我的经验,11年,谁知道后来有多少C#/ C++版本.

当然,这并非巧合,因为这两种语言在设计目标上有很大的不同.C#希望在开发成本是主要考虑因素(仍然是大多数软件)的情况下使用,并且C++会让您节省不用任何费用来挤出机器的每一次性能:游戏,算法交易,数据 - 中心等


Tho*_*ews 6

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

在现代,应用程序或执行速度不像以前那么重要。开发进度、正确性和稳健性是更高的优先级。如果应用程序的高速版本有很多错误、经常崩溃或更糟、错过了进入市场或部署的机会,那么它就不好。

由于开发进度是重中之重,因此新语言的出现可以加快开发速度。C# 就是其中之一。C# 还通过删除 C++ 中导致常见问题的功能来帮助提高正确性和鲁棒性:一个例子是指针。

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

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


Ale*_* C. 5

C#比C++更快.写得快.对于执行时间,没有什么比分析器更好.

但是C#没有像C++那样容易接口的库.

C#在很大程度上取决于Windows ......

  • C#需要来自编译器,操作系统和平台的更多支持.嵌入式系统的一个巨大问题. (4认同)
  • 这个答案现在已经改变了。Microsoft 目前正式支持 .NET Standard 运行时在 Linux 上运行。因此,除非您必须在微控制器上进行开发,否则可移植性根本不是问题。更不用说它也变得更快了。 (2认同)