使用for循环的矩阵乘法会降低性能吗?

时间:2014-08-26 19:51:12

标签: c++ performance loops matrix

目前我正在开发一个使用矩阵的程序。我想出了这个嵌套循环来乘以两个矩阵:

// The matrices are 1-dimensional arrays
for (int i = 0; i < 4; i++)
    for (int j = 0; j < 4; j++)
        for (int k = 0; k < 4; k++)
            result[i * 4 + j] += M1[i * 4 + k] * M2[k * 4 + j];

循环有效。我的问题是:与将其全部手动写完相比,这个循环会慢吗:

result[0] = M1[0]*M2[0] + M1[1]*M2[4] + M1[2]*M2[8] + M1[3]*M2[12];
result[1] = M1[0]*M2[1] + M1[1]*M2[5] + M1[2]*M2[9] + M1[4]*M2[13];
result[2] = ... etc.

因为在嵌套循环中,计算数组位置,而在第二种方法中,它们不会。

感谢。

4 个答案:

答案 0 :(得分:4)

与许多事情一样,“它取决于”,但在这种情况下,我倾向于第二种,扩展形式表现几乎相同。任何现代编译器都会为您展开适当的循环,并处理它。

两点可能值得一提:

  1. 第二种方法更加丑陋,更容易出错,写作/维护也很乏味。

  2. 这是'premature optimization'的一个很好的例子(AKA是万恶之源)。你知道这部分是否是一个瓶颈?这真的是代码中最密集的部分吗?通过如此早地进行优化,如果我们没有标记我们的代码,那么我们就会在#1点产生所有内容,以达到预感。

答案 1 :(得分:0)

您的编译器可能已经这样做了,请查看loop unrolling。 让编译器完成猜测和繁重的工作,坚持使用干净的代码,并一如既往地衡量您的表现。

答案 2 :(得分:0)

我认为循环不会慢。在两种情况下,您都以相同的方式访问M1和M2阵列的存储器,即。如果你想更快地制作“手动”版本,那么使用标量替换并对寄存器进行计算,例如

 double M1_0 = M1[0];
 double M2_0 = M2[0];
 result[0] = M1_0*M2_0 + ...

但你也可以在循环中使用标量替换。如果你进行阻塞和循环展开,你可以这样做(实际上你的三重循环看起来像MMM的阻塞版本)。

您要做的是通过改善位置来加速程序,即更好地利用内存层次结构和更好的位置。

答案 3 :(得分:0)

假设您在英特尔处理器或兼容(AMD)上运行代码,您实际上可能希望切换到汇编语言来执行繁重的矩阵计算。幸运的是,您拥有Intel-IPP库,可以使用高级处理器技术为您完成实际工作,并根据您的处理器选择最快的算法。

IPP包括您可能需要的所有必要的矩阵计算。您可能遇到的唯一问题是您创建矩阵的顺序。您可能需要重新组织订单,以便更轻松地使用您想要使用的IPP功能。

请注意,关于您的两个代码示例,第二个代码示例会更快,因为您避免使用+=运算符,这是一个读取/修改/写入周期,并且通常很慢(不仅如此,它还需要结果矩阵是全部为零开始,而第二个例子不需要先清除输出),尽管你的矩阵可能适合缓存...但是,处理器被优化以按顺序读取输入数据(a [ 0],一个1,一个[2],一个[3],...),并且还要按顺序写回数据。如果您可以将算法编写为尽可能接近这样的序列,那就更好了。不要误解我的意思,我知道矩阵乘法不能按顺序进行。但是如果你想到这样做你的优化,你将获得更好的结果(即改变你的矩阵在内存中保存的顺序可能是其中之一)。