C++和C#在数学方面的性能差异

16

首先声明,我并不想挑起争端。我想知道有没有好的资源可以比较C++和C#在数学密集型代码方面的表现?我的第一印象是C#应该会慢得多,但这只是我的主观感受,没有实际证据支持。我想知道是否有人做过相关的研究或测试?我计划自己进行一些测试,但希望知道是否有人以严谨的方式进行了此类测试(谷歌搜索结果很少)。谢谢。

编辑:我所指的密集型是指在紧密循环中大量使用sin/cos/exp等函数。


1
说句实话,有时候你可以使用未经检查的运算符来提高 C# 的速度:http://msdn.microsoft.com/en-us/library/aa691349%28VS.71%29.aspx - Juliet
2
我会建议使用更适合的那个。如果是C#,而且你遇到了性能问题,你仍然可以在C++甚至汇编语言中重写瓶颈部分。 - Tamás Szelei
3
数值计算?你考虑过FORTRAN吗?http://www.lahey.com/lf71/lfnet.htm - Remus Rusanu
3
+1 Remus -- 当车库里有一辆法拉利时,我们在比较野马和卡玛罗... - Austin Salonen
4
@Austin: 喜欢那个比喻。当然......法拉利有四把不同的钥匙,每个钥匙必须在精确的时刻插入并转动,否则这该死的东西就不会启动。哦,离合器在座椅后面。 - Randolpho
显示剩余4条评论
9个回答

13

C# 总体上会比较慢,但差别不是非常显著。在某些情况下,根据代码结构,C# 实际上可以更快,因为 JIT 分析经常可以提高长时间运行算法的性能。

编辑:这里有一篇很好的关于C#与C++性能的讨论

编辑2:

“总的来说”并不十分准确。正如您所说,JIT编译器实际上可以将MSIL编译成比C++编译器更快的本地代码,因为它可以针对正在运行的硬件进行优化。

然而,JIT编译本身是资源密集型的,并且在托管代码中会发生运行时检查。 预编译和预优化的代码将始终比仅 JIT 编译的代码更快。 每个基准测试比较都表明了这一点。但是,长时间运行的进程可能需要进行相当多的运行时分析,这可以改善优化过的本地代码。

因此,我所说的是100%准确的。对于一般情况,托管代码比预编译和预优化的代码稍慢。然而,对于某些情况,JIT分析可以提高性能,胜过预优化的本地代码。


2
“一般来说”并不是很准确。就像你所说,JIT编译器可以将MSIL编译成比C++编译器更快的本地代码,因为它可以针对其正在运行的硬件进行优化。 - Ed S.
4
当然,由于运行时检查,会有一些惩罚...因此,“通常”可能并不会误导 :) - Ed S.
2
哦,实际上你提到了“在某些情况下,JIT分析可以比预优化的本地代码提高性能。”我同意这一点 :) 只是你之前的引用(我上面提到的)似乎与此相矛盾。 - Falaina
2
那个所谓的“好讨论”太烂了(我是C++程序员)。他正在使用std::sort来对数据结构进行排序,而实际上是通过引用接受数组。与其在C#中做同样的事情(Java足够聪明,可以将调用new作为堆栈分配,当对象范围限制在函数时 - 我相信C#也是类似的),他每次都要复制一遍,然后惊讶地发现性能很差。如果你写烂代码,你的性能就会很差。 - Vitali
2
@Martin York:优化C++代码并不是关于编译器开关/标志的问题,而更多地涉及到编译器的能力和代码结构。编译器只能以某种特定的方式解释某些代码块;例如,一个结构不良的循环不太可能被编译器展开。即使有提示,内联也不能保证。另一方面,JIT分析可以查看循环执行的历史记录,并修改编译以展开它,或选择内联比编译器更大的方法。这总是有效吗?不是。但是这个可能性确实存在。 - Randolpho
显示剩余7条评论

13

作为我的工作的一部分,我需要定期比较各种语言和运行时下的核心数学性能。

在最近的一次测试中,在一个关键基准测试下——将由4D矩阵对长数组的4D向量进行变换并进行最终归一化的操作中——与我优化后的C++控制组相比,C#的表现约慢了30倍。在我的C++代码中,我可以获得每1.8ns处理一个向量的峰值吞吐量,而C#处理一个向量大约需要65ns的时间。

当然,这是一个特殊情况,而且C++代码并不是朴素的:它使用了软件管线、SIMD、缓存预取等全部微观优化技巧。


3
很高兴能够帮忙,但请记住我在引用一个特定的案例——一种特定类型的线性代数,在这里我正在比较C#的JIT输出和我精心手动优化过几乎等同于汇编的C++。 - Crashworks

6
你没有很好地定义“数学密集型”(轻描淡写地说:根本没有)。我们试图进行一些分解:
- 对于基本的Sin / Cos / Log函数,我不会期望有太大的差别。 - 对于线性代数(矩阵),我会预计.NET会失利,数组上的(始终强制执行的)边界检查只在某些情况下被优化掉。 - 你可能需要对接近你预期领域进行基准测试。

5

对于纯数学函数来说,问C#是否比C++更快并不是最好的问题。你应该问的是:

CLR JITer生成的汇编代码是否比C++编译器生成的汇编代码更有效率。

C#编译器对于纯数学运算的速度影响较小,而CLR JIT却有很大的影响。如果关闭溢出检查,它的性能几乎与其他.NET语言(如VB.Net)相同。


5

据我所知,微软的许可证不允许发布基准测试结果。 - igouy

5

我认为你问错了问题。你应该询问C++在数学计算方面是否可以击败.NET语言家族。看一下Runge Kutta的F#时间比较。


是的,那就是我的意思,我明白所有的.NET语言都会转换成IL代码,然后再转换成机器码。 - Steve
使用正确的语言完成工作会得到 +1。链接中有很好的证据。 - Austin Salonen
就此而言,似乎 F# 不仅可以轻松地转换为并行进程,还可以优化重复性任务,例如您在该链接中看到的内容。我想说,它并不像链接所描述的那样神奇,但在解释语言上实现这一点确实非常令人兴奋。 - wheaties

2

我建议考虑使用Mono.Simd来加速一些操作。缺点是在微软运行时上它没有被加速。


2
我最近没有检查过,但上一次我检查时,微软的 .NET 运行时许可协议要求您同意不发布任何性能基准测试。这往往会限制发布的实际信息量。
有些人暗示了这一点,但我会直接说出来:我认为你正在进行(极度)过早的优化 - 或者试图这样做。
编辑: 稍微找了一下,许可证已经改变了(事实上很久以前)。当前条款 表示您可以发布基准测试 - 但仅在满足其条件的情况下才能这样做。其中一些条件看起来(对我来说)几乎不可能满足。例如,只有在“使用产品文档和/或微软支持网站中提供的所有性能调整和最佳实践指导”的情况下才能发布基准测试。考虑到微软网站的规模和数量,我不知道任何人有机会确保他们遵循了他们可能提供的所有指导。
尽管该网页谈论的是 .NET 1.1,但较新的许可证似乎也参照它。
所以,我记得的技术上是错误的,但从实际效果上来说还是正确的。

1
好的,如果针对数学上复杂的代码有2倍的减速,那么这并不是过早的。这样可以避免时间陷阱。 - Steve
完全没有回答问题,-1。 - Ed S.
1
杰瑞,你有那个限制的链接(或相关讨论)吗? - H H
Ed,它非常好地回答了这个问题(有没有任何研究……)。 - H H

1

对于基本的数学库函数而言,C# 调用的是与 C++ 相同的编译代码,因此差异不大。但对于一些在数学库中没有找到的有趣数学问题,C# 会存在几个劣势。目前的 JIT 不支持 SSE 指令,而这是在 C++ 中可以使用的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接