为什么在.NET中矩阵乘法如此缓慢?

15
我不太明白为什么在C#/.NET(甚至Java)中矩阵乘法如此缓慢。请看这个基准测试(source):试图找到一个更新的基准测试。

Java vs C# vs C++ breakdown

C#的整数和双精度性能非常接近使用MSVC++编译的C++。双精度为87%,32位整数为99%。相当不错,我想说。但是看看矩阵乘法。差距扩大了,C#变成了约19%的速度。这是一个相当大的差异,我不理解。矩阵乘法只是一堆简单的数学运算。它为什么会变得如此缓慢?它不应该与等效数量的简单浮点或整数操作大致相同吗?
这在游戏和XNA方面尤为关注,其中矩阵和向量性能对于物理引擎等关键。一段时间以前,Mono通过一些漂亮的向量和矩阵类添加了对SIMD指令的支持。它弥合了差距,使Mono比手写的C++更快,尽管没有C++加上SIMD快。(来源

Matrix multiplication comparison

这里发生了什么?

编辑:仔细看了一下,我误读了第二个图表。C# 程序看起来非常接近。难道第一个基准测试做错了什么吗? 对不起,我错过了第一个基准测试的版本号。我只是抓取它作为“C# 线性代数很慢”这一说法的便捷参考。我会尝试找另一个。


2
C# 版本 + 选项:.Net Framework 1.1.4322嗯... 没有更新版本吗? - GalacticJello
5
“坐等JonSkeet发表意见” :-) - WestDiscGolf
@Matt:说得好。6年前的基准测试,而且还是针对那些过时的框架/语言版本,基本上没有什么用处了。 - cHao
@cHao:你说得对,我同意这是.NET的一次全面改革,但我认为像这样相对简单的东西的性能应该在IL级别上,我怀疑它没有做太多工作(虽然我可能错了)。 - Armstrongest
@Gary '-': 泛型需要从底层得到支持,因此我相信至少对IL翻译器/JIT进行了一些修改。即使没有这样做,IL只是半消化的代码。实际运行它的是CLR本身(经历了一些相当大的变化)。 - cHao
显示剩余6条评论
4个回答

14

对于这样的大矩阵,CPU缓存成为了限制因素。最重要的是如何存储矩阵。而且基准测试代码在比较苹果和橙子。C++代码使用不规则数组,C#代码使用二维数组。

将C#代码改写为使用不规则数组后,速度翻倍。避免数组索引边界检查来重写矩阵乘法代码似乎毫无意义,因为没有人会在实际问题中使用这样的代码。


谢谢,这样就清楚了。那么为什么我总是听到(除了其他原因之外)“XNA很慢,因为C#中的矩阵乘法很慢”?这不是真的吗? - Matthew Olenik
1
我不知道,就我所知,那是一个无法证实的说法。XNA程序员经常编写自己的矩阵乘法代码吗?当涉及速度时,C/C ++代码是毫不妥协的,如果爆炸了,你会被碎片击中耳朵。如果在C#中出现特定算法的速度问题,则始终可以退而求其次使用C/C++。 - Hans Passant
1
不,他们使用XNA提供的库。 - Matthew Olenik

11
为了解释XNA矩阵操作速度缓慢的原因:
首先是初学者易错点:XNA的Matrix类的运算符*会生成多个副本。这比等效的C++代码要慢。
(当然,如果你使用Matrix.Multiply(),那么可以通过引用传递参数。)
其次是在Xbox 360上使用XNA时,.NET Compact Framework无法访问VMX硬件(SIMD),而这是本地C++游戏所能使用的。
这就是为什么你总是听到它很慢的原因。但是从你发布的基准测试结果可以看出,如果你对比一下苹果和苹果,其实速度并不真的很“慢”。

1
有道理。也许一些误解来自于使用运算符。 - Matthew Olenik

8
显然,基准测试的作者没有理解C#中锯齿数组和多维数组之间的区别。这真的不是一种苹果与苹果的比较。当我将代码更改为使用锯齿数组而不是多维数组时,它的运行方式更类似于Java,那么C#代码的运行速度会快两倍...使它比Java更快(虽然仅略微,可能在统计上没有显著差异)。在C#中,多维数组速度较慢,因为查找数组槽需要额外的工作,并且它们的数组边界检查无法被消除...但目前还不能。

请参见此问题以获取有关为什么多维数组比锯齿数组慢的更深入分析。

请参见此博客以获取有关数组边界检查的更多信息。该文章特别警告不要使用多维数组进行矩阵乘法。


4
这是一个涉及矩阵乘法的更新基准测试(以及使用新的任务并行库进行的一些基准测试):
文章介绍了不同的方法,并解释了为什么多维数组是一个糟糕的选择:
“用 .NET 中的多维数组和 i、j、k 循环顺序做矩阵乘法是最简单的方法。但问题有两个。首先,i、j、k 循环顺序访问内存的方式很混乱,导致数据从各种位置被拉取进来。其次,它使用了多维数组。是的,.NET 的多维数组很方便,但速度非常慢。” 使用任务并行库 (TPL) 进行并行矩阵乘法

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