用于循环的矩阵乘法会降低性能吗
Will matrix multiplication using for loops decrease performance?
目前我正在开发一个使用矩阵的程序。我想出了这个嵌套循环来乘以两个矩阵:
// 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.
因为在嵌套循环中,会计算数组位置,而在第二种方法中,则不会计算。
谢谢。
与许多事情一样,"这取决于",但在这种情况下,我倾向于使用第二种扩展形式来执行大致相同的功能。任何现代编译器都会为您展开适当的循环,并处理好它
两点也许值得一提:
-
第二种方法更丑陋,更容易出错,编写/维护起来也很乏味。
-
这是一个"过早优化"(又名万恶之源)的好例子。你知道这个部分是否是一个瓶颈吗?这真的是代码中最密集的部分吗?通过这么早的优化,如果我们没有对代码进行基准标记,我们会在第1点中产生所有的东西,这相当于一种预感。
您的编译器可能已经完成了这项工作,请查看循环展开。让编译器做猜测和繁重的工作,坚持干净的代码,并一如既往地衡量您的性能。
我认为循环不会变慢。您在两种情况下都以相同的方式访问M1和M2阵列的内存,即。如果您想使"手动"版本更快,请使用标量替换并在寄存器上进行计算,例如
double M1_0 = M1[0];
double M2_0 = M2[0];
result[0] = M1_0*M2_0 + ...
但是您也可以在循环中使用标量替换。如果您进行阻塞和循环展开,就可以做到这一点(事实上,您的三重循环看起来像MMM的阻塞版本)。
您要做的是通过改进局部性来加快程序的速度,即更好地使用内存层次结构和更好的局部性。
假设您在英特尔处理器或兼容(AMD)上运行代码,您可能实际上想要切换到汇编语言来进行繁重的矩阵计算。幸运的是,您有"英特尔IPP"库,它可以使用先进的处理器技术为您完成实际工作,并根据您的处理器选择最快的算法。
IPP包括您可能需要的所有必要的矩阵计算。你可能遇到的唯一问题是你创建矩阵的顺序。您可能需要重新组织订单,以便更容易地使用您想要使用的IPP功能。
请注意,就您的两个代码示例而言,第二个示例会更快,因为您避免了+=
运算符,这是一个读/修改/写周期,而且通常很慢(不仅如此,它还要求结果矩阵从零开始,而第二个实例不需要先清除输出),尽管您的矩阵可能适合缓存。。。但是,处理器被优化为按顺序读取输入数据(a[0],a1,a[2],a[3],…),并且还按顺序写回该数据。如果你能把你的算法写得尽可能接近这样一个序列,那就更好了。不要误解我的意思,我知道矩阵乘法不能按顺序进行。但是,如果你考虑这样做来进行优化,你会获得更好的结果(即,改变矩阵在内存中的保存顺序可能就是其中之一)。
- 与多个 for 循环与单个 for 循环 wrt 相关的性能从多映射获取数据
- 基于范围的 for 循环range_declaration中各种说明符之间的性能差异
- C++中循环的性能差异
- C++循环性能的倍数
- 在原始循环上使用boost::irange的性能损失
- OpenMP 嵌套循环处理性能
- 虚拟函数调用的性能作为 for 循环中的上限
- 在 C++ 中使用 OpenMP 并行化两个 for 循环不会提供更好的性能
- C++和Java的字符串循环性能比较
- C++:if 内部循环的性能影响
- 为什么在循环外举起弦会导致性能较慢
- openMp的多个独立for循环的性能问题
- C 的性能11现代风格的循环与老式循环
- 用于C++和性能关键型应用程序中的循环
- 奇怪的OpenCL调用C++上的副作用来提高循环性能
- 为什么asm中的这种差异对性能很重要(在未优化的ptr++与++ptr循环中)
- 具有相同索引的循环的性能
- 提高循环缓冲区或堆排序性能
- 如何在测量性能时减少循环的开销
- 性能:循环声明与循环重新初始化