为什么.NET中的矩阵乘法如此之慢?

时间:2010-07-12 14:39:26

标签: c# .net performance xna matrix-multiplication

我不太明白是什么让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#线性代数很慢”的方便参考。我会试着找另一个。

4 个答案:

答案 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   多维数组很方便,   但它很慢。