目前我正在开发一个使用矩阵的程序。我想出了这个嵌套循环来乘以两个矩阵:
// 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.
因为在嵌套循环中,计算数组位置,而在第二种方法中,它们不会。
感谢。
答案 0 :(得分:4)
与许多事情一样,“它取决于”,但在这种情况下,我倾向于第二种,扩展形式表现几乎相同。任何现代编译器都会为您展开适当的循环,并处理它。
两点可能值得一提:
第二种方法更加丑陋,更容易出错,写作/维护也很乏味。
这是'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],...),并且还要按顺序写回数据。如果您可以将算法编写为尽可能接近这样的序列,那就更好了。不要误解我的意思,我知道矩阵乘法不能按顺序进行。但是如果你想到这样做你的优化,你将获得更好的结果(即改变你的矩阵在内存中保存的顺序可能是其中之一)。