C++矩阵计算效率

5
我们正在尝试优化我们的C++代码,以下是使用Eigen库进行矩阵计算的代码:
#include<Eigen/Dense>

int main(){

   MatrixXd P = MatrixXd::Random(30,30); // a  random double 30 x 30 matrix P
   MatrixXd M = MatrixXd::Random(30,30); // a  random double 30 x 30 matrix M
   Matrix<double, 30, 30> I; 
   I.setIdentity(); // I is an 30 x 30 identity matirx

   P = (I-M)*P

   return 0;

   }

其中它们都是n x n矩阵,I是单位矩阵。 我们发现重写上述矩阵计算

   P= (I- M)*P

作为

   P = P-M*P

在使用gcc 6.2编译器的Linux Ubuntu系统中,可以实现4-8倍的速度提升。我意识到编译器可能不知道什么是单位矩阵和I * P = P这个事实,但我仍然无法理解是什么原因导致了效率的如此大幅提高。有人知道可能导致如此显著改进的原因吗?


4
我不是专家,但仅使用P、M听起来比使用I、M、P有更好的缓存行为。遗憾的是,这些优化非常复杂(考虑到目标体系结构),我认为您矩阵的实际大小(以及可能的内部类型)也很重要! - sascha
3
第二个版本很可能可以通过单个函数调用与没有临时变量的方式匹配,例如 dgemm http://www.netlib.org/lapack/lapack-3.1.1/html/dgemm.f.html ,而第一个版本无法通过单个函数匹配,因此需要使用临时变量进行计算(首先计算 I - M,然后乘以 P 并替换旧值)。 - alfC
3
请提供一个最小化可重现示例,否则我们只能猜测。同时,请发布您的平台和编译方式。如果您能够发布反汇编结果,那将更有帮助。 - xaxxon
谢谢您的建议。我会加入更多的代码! - SunnyIsaLearner
请你使用以下命令编译你的文件,并比较生成的汇编文件(.s)。(编译命令:g++ -O2 -S foo.c) - Mahmoud Fayez
“GNC编译器”是什么意思?也许是指GNU C++编译器(即g++)?如果是的话,您使用的是哪个版本?(这可能实际上与此处无关,但通常在有关性能的问题中应该提到)。 - chtz
1个回答

1
首先,I.identity();不存在。你需要的是I.setIdentity()或者P = (MatrixXd::Identity(30,30)-M)*P
如果你选择第一种选项,Eigen肯定需要进行完整的30x30减法运算(编译器很难看到与第二个表达式的等价性)。总体而言,这将导致两个临时变量(一个用于差值,一个用于乘积)。
如果你实际上使用了I.Identity(),你就像调用成员函数一样调用了静态函数,你的编译器应该至少会警告你。这实际上不会修改I,并且你最终会得到未初始化的值I,其中可能包括一些NaN或非规格化值,这两者都可能对浮点性能产生负面影响。当然,你的结果也会出错。
总的来说,我认为编写方程的最简单方法是:
P -= M*P;

或者

MatrixXd Pnew = P - M*P;

感谢您指出错误(是的,应该是setIdentity(),我试图在移动设备上编辑帖子,犯了一个大错误),并且您的解释。我修改了我的帖子。 - SunnyIsaLearner

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