我不太明白是什么让C#/ .NET(甚至Java)中的矩阵乘法变得如此缓慢。
看看这个基准(source):试图找到更新的基准。
Java vs C# vs C++ breakdown http://img411.imageshack.us/img411/9324/perf.gif
C#的整数和双重性能非常接近使用MSVC ++编译的C ++。双精度为87%,32位整数为99%。非常好,我会说。但接下来看矩阵乘法。差距扩大到C#的速度约为19%。这是一个非常大的差异,我不明白。矩阵乘法只是一堆简单的数学运算。怎么这么慢?它不应该与等效数量的简单浮点或整数运算一样快吗?
这尤其是游戏和XNA的关注点,其中矩阵和矢量性能对物理引擎等事物至关重要。前一段时间,Mono通过一些漂亮的矢量和矩阵类增加了对SIMD指令的支持。它缩小了差距,使Mono比手写的C ++更快,尽管没有C ++与SIMD一样快。 (source)
Matrix multiplication comparison http://img237.imageshack.us/img237/2788/resultse.png
这里发生了什么?
编辑:仔细观察,我误读了第二张图。 C#看起来非常接近。 第一个基准测试只是做了可怕的错误吗?抱歉,我错过了第一个基准测试版的版本号。我抓住它作为我总是听到的“C#线性代数很慢”的方便参考。我会试着找另一个。
答案 0 :(得分:13)
对于像这样的大型矩阵,CPU缓存成为限制因素。什么是非常重要的是矩阵的存储方式。基准代码是比较苹果和橙子。 C ++代码使用锯齿状数组,C#代码使用二维数组。
重写C#代码以使用锯齿状数组,并使其速度加倍。重写矩阵乘法代码以避免数组索引边界检查似乎毫无意义,没有人会使用这样的代码来解决实际问题。
答案 1 :(得分:10)
解释XNA矩阵运算缓慢的想法的起源:
首先是初学者级别的问题:XNA Matrix
班级的operator*
将制作多个副本。这比您对等效C ++代码的期望要慢。
(当然,如果您使用Matrix.Multiply()
,则可以通过引用传递。)
第二个原因是XNA在Xbox 360上使用的.NET Compact Framework无法访问本机C ++游戏可用的VMX硬件(SIMD)。
这就是为什么你一直听说它很慢,至少。从你发布的基准测试中可以看出 - 当你比较苹果和苹果时,它并不是那么“慢”。
答案 2 :(得分:7)
很明显,基准作者不了解C#中的锯齿状和多维数组之间的区别。比较真的不是一个苹果对苹果。当我更改代码以使用锯齿状数组而不是多维数组以便它以类似于Java的方式运行时,C#代码最终运行速度提高了两倍...使其比Java快(尽管只是勉强可能统计上无关紧要的)。在C#中,多维数组的速度较慢,因为查找数组插槽需要额外的工作,因为无法为它们消除数组边界检查...
请参阅此question以更深入地分析多维数组比锯齿状数组慢的原因。
有关数组边界检查的更多信息,请参阅此blog。本文特别警告不要使用多维数组进行矩阵乘法。
答案 3 :(得分:3)
这是一个处理矩阵乘法的更新基准(以及使用新的任务并行库的一些基准):
Parallel Matrix Multiplication with the Task Parallel Library (TPL)
本文介绍了不同的方法,并解释了为什么多维数组的选择不好:
做矩阵的最简单方法 乘法是用.NET 具有i,j,k的多维数组 在循环中排序。问题 是双重的。首先,i,j.k订购 以忙碌的方式访问记忆 导致不同位置的数据 拉进来。第二,它正在使用 多维数组。是的,.NET 多维数组很方便, 但它很慢。