C#真的比说C ++慢吗?
我一直在想这个问题一段时间了。
当然,C#中的某些东西并未针对速度进行优化,因此使用这些对象或语言调整(如LinQ)可能会导致代码变慢。
但是,如果您不使用任何这些调整,只是比较C#和C ++中的相同代码片段(很容易将其转换为另一个)。 它真的会慢得多吗?
我看过一些比较表明C#在某些情况下可能会更快,因为理论上JIT编译器应该实时优化代码并获得更好的结果:
托管还是非托管?
我们应该记住,JIT编译器会实时编译代码,但这是一次性开销,相同的代码(一旦达到并编译)不需要在运行时重新编译。
GC不会增加很多开销,除非您创建并销毁数千个对象(如使用String而不是StringBuilder)。 而在C ++中这样做也会很昂贵。
我想提出的另一点是在.Net中引入的DLL之间更好的通信。 .Net平台的通信比托管的基于COM的DLL要好得多。
我没有看到语言应该更慢的原因,我真的不认为C#比C ++慢(从经验和缺乏良好的解释)。
那么,用C#编写的相同代码会比C ++中的相同代码慢吗?
如果是这样,那么为什么?
其他一些参考资料(其中有一些讨论,但没有解释为什么):
为什么你要使用C#,如果它比C ++慢?
警告:您提出的问题确实非常复杂 - 可能比您意识到的要多得多。 因此,这是一个非常长的答案。
从纯粹的理论观点来看,可能有一个简单的答案:C#没有(可能)没有真正阻止C ++的速度。 然而,尽管有这样的理论,但在某些情况下,在某些情况下会有一些实际的原因。
我会考虑三个不同的基本区域:语言功能,虚拟机执行和垃圾收集。 后两者经常走在一起,但可以是独立的,所以我会分别看看他们。
语言功能
C ++非常重视模板,模板系统中的特性主要是为了尽可能地在编译时完成,所以从程序的角度来看,它们是“静态的”。 模板元编程允许在编译时完全执行任意计算(即,模板系统是图灵完整的)。 因此,基本上不依赖于用户输入的任何内容都可以在编译时计算出来,所以在运行时它只是一个常量。 但是,对此的输入可以包含类型信息之类的东西,因此,通过C#中的模板元编程,编译时通常会在C#中通过反射来完成大量的工作。 尽管运行速度和多功能性之间肯定存在折衷 - 模板可以做什么,它们是静态的,但它们不能做任何反射。
语言特征的差异意味着,几乎任何仅仅通过将C#转译成C ++(反之亦然)来比较两种语言的尝试都可能会产生无意义和误导之间的结果(对于大多数其他语言对也是如此以及)。 简单的事实是,对于任何大于几行代码的东西,几乎没有人可能以相同的方式使用这些语言(或者以相同的方式足够接近),以至于这样的比较告诉你有关这些语言的方式在现实生活中工作。
虚拟机
与几乎任何合理的现代化虚拟机一样,微软的.NET可以并将执行JIT(又名“动态”)编译。 但这代表了一些权衡。
首先,优化代码(像大多数其他优化问题一样)主要是NP完全问题。 除了一个真正微不足道的/玩具程序,你几乎可以保证你不会真正“优化”结果(也就是说,你不会找到真正的最佳) - 优化器只会使代码更好之前是。 然而,众所周知的一些优化需要花费大量的时间(并且通常是内存)来执行。 使用JIT编译器,用户正在等待编译器运行。 排除大多数昂贵的优化技术。 静态编译有两个优点:首先,如果速度慢(例如,构建大型系统),它通常在服务器上执行,而没有人花时间等待它。 其次,一个可执行文件可以生成一次,并且被很多人使用很多次。 第一个最小化优化成本; 第二个分摊比更大数量的执行更小的成本。
正如原始问题(以及许多其他网站)中提到的那样,JIT编译确实有可能更好地了解目标环境,这应该(至少在理论上)抵消这种优势。 毫无疑问,这个因素至少可以抵消部分静态编译的缺点。 对于一些特定类型的代码和目标环境,它甚至可以超过静态编译的优势,有时甚至是相当戏剧性的。 但至少在我的测试和经验中,这是非常不寻常的。 依赖于目标的优化大多数似乎要么做出相当小的差异,要么只能(自动地,无论如何)应用于相当具体的问题类型。 如果你在一台现代化的机器上运行一个相对较旧的程序,这种情况将会发生。 用C ++编写的旧程序可能已被编译为32位代码,即使在现代的64位处理器上也会继续使用32位代码。 用C#编写的程序将被编译为字节码,然后虚拟机将编译为64位机器码。 如果这个程序从64位代码运行中获得了实质性好处,那么这可能会带来很大的优势。 在64位处理器相当新的很短时间内,这发生了相当的数量。 最近可能受益于64位处理器的代码通常可以静态编译为64位代码。
使用虚拟机还有可能改善缓存使用率。 虚拟机的指令通常比原生机器指令更紧凑。 它们中的更多可以适应给定数量的高速缓存,所以当需要的时候,任何给定代码都处于高速缓存中的机会更大。 这可以帮助保持VM代码的解释执行比大多数人最初期望的更具竞争力(速度方面) - 在一次缓存未命中的时间内,您可以在现代CPU上执行大量指令。
还值得一提的是,这两个因素并不一定是完全不同的。 没有什么能够阻止(例如)C ++编译器生成打算在虚拟机上运行的输出(带或不带JIT)。 事实上,微软的C ++ / CLI几乎就是这样 - 一个(几乎)符合C ++编译器(虽然有很多扩展),它们产生的目的是在虚拟机上运行。
反过来也是如此:微软现在拥有.NET Native,它将C#(或VB.NET)代码编译为本机可执行文件。 这使得性能通常更像C ++,但保留了C#/ VB的特性(例如,编译为本机代码的C#仍然支持反射)。 如果你有性能密集的C#代码,这可能会有所帮助。
垃圾收集
从我所看到的情况来看,我认为垃圾收集是这三个因素中最贫穷的一个因素。 只是一个明显的例子,这里的问题提到:“GC不会增加很多开销,除非你创建和销毁数千个对象[...]”。 实际上,如果您创建并销毁数千个对象,垃圾收集的开销通常会相当低。 .NET使用了一代清道夫,这是一种复制收集器。 垃圾收集器通过从“地方”(例如,寄存器和执行堆栈)开始工作,即已知指针/引用可被访问。 然后它“追逐”那些已经分配到堆上的对象的指针。 它检查这些对象是否有更多的指针/引用,直到它将所有对象都追踪到任何链的末尾,并找到所有(至少可能)可访问的对象。 在下一步中,它会使用所有(或至少可能会)使用的对象,并通过将所有对象复制到堆中管理的内存的一端的连续块中来压缩堆。 剩下的内存是免费的(模块化终结器必须运行,但至少在编写良好的代码中,它们非常稀少,以至于我暂时忽略它们)。
这意味着如果您创建并销毁大量对象,垃圾收集会增加很少的开销。 垃圾收集周期所花费的时间几乎完全取决于已创建但未销毁的对象的数量。 快速创建和销毁对象的主要后果就是GC必须更频繁地运行,但每个周期仍然会很快。 如果您创建对象并且不销毁它们,那么GC将更频繁地运行,并且每个周期都会大大减慢,因为它花费更多时间来追踪指向潜在活动对象的指针,并且花费更多时间复制仍在使用中的对象。
为了解决这个问题,世代清除工作的假设是,一直保持“活着”状态的物体可能会持续很长一段时间。 基于此,它有一个系统,在这个系统中,存活一定数量的垃圾收集周期的对象变得“终身”,并且垃圾收集器开始简单地假定它们仍在使用中,所以不是在每个周期都复制它,而是简单地离开他们一个人。 这是一个有效的假设,经常足以使世代清除典型地具有比大多数其他形式的GC低得多的开销。
“手动”内存管理通常同样难以理解。 举一个例子,许多比较尝试都假设所有的手动内存管理都遵循一个特定的模型(例如,最适合的分配)。 与许多人关于垃圾收集的信念(例如,通常使用引用计数进行的广泛假设)相比,这通常很少(如果有的话)更接近现实。
鉴于垃圾收集和手动内存管理的各种策略,在总体速度方面比较两者是非常困难的。 试图比较分配和/或释放内存的速度(本身)几乎可以保证产生最好没有意义的结果,最坏的情况是彻底误导。
奖金主题:基准
由于不少博客,网站,杂志文章等都声称以某种方向提供“客观”证据,所以我也会在这方面投入2分钱。
这些基准中的大部分有点像青少年决定比赛他们的车,谁赢谁得到保留两辆车。 尽管如此,网站的重要性不同:他们发布基准测试的人会驾驶两辆车。 由于一些奇怪的机会,他的赛车总是赢得胜利,而其他人都不得不相信“相信我,我真的很快就开着你的赛车。”
编写一个糟糕的基准很容易,它产生的结果几乎毫无意义。 几乎任何人都可以在任何地方接近设计一个能够产生任何有意义的基准的必要技能,他们也有能力制作出能够达到他想要的结果的技能。 事实上,编写代码来产生特定结果可能比编写真正产生有意义结果的代码更容易。
正如我的朋友James Kanze所说的那样,“永远不要相信你没有伪造自己的基准。”
结论
没有简单的答案。 我相当肯定地认为,我可以掷硬币来选择赢家,然后从1到20之间挑选一个数字来表示赢得比例的百分比,然后编写一些看似合理和公平的基准的代码,以及(至少在某些目标处理器上 - 不同的处理器可能会稍微改变一个百分比)。
正如其他人所指出的那样,对于大多数代码来说,速度几乎无关紧要。 对此的推论(更常被忽略)是在速度很重要的小代码中,它通常很重要。 至少在我的经验中,对于代码真正重要的代码,C ++几乎总是胜利者。 肯定有一些支持C#的因素,但实际上它们似乎超过了支持C ++的因素。 你当然可以找到基准来表明你选择的结果,但是当你编写真正的代码时,你几乎总是可以在C ++中使它比C#更快。 它可能(或可能不)需要更多的技巧和/或努力来编写,但它几乎总是可能的。
因为你并不总是需要使用(而且我使用这个松散的)“最快”的语言? 仅仅因为速度更快,我不会开车去法拉利工作......
C ++总是有性能优势。 使用C#,我无法处理内存,并且我有大量资源可用于完成我的工作。
你需要质疑自己更多的是关于哪一个节省你的时间。 机器现在非常强大,并且大部分代码都应该用一种语言来完成,这种语言可以让您在最短的时间内获得最大的价值。
如果在C#中有一个核心处理过程耗时过长,则可以使用C#构建一个C ++并与之互操作。
停止考虑你的代码性能。 开始创造价值。
链接地址: http://www.djcxy.com/p/85415.html