我正在一个C++程序中进行许多矩阵乘法,并且我使用Eigen (3.3.5)与Intel的MKL (2018.3.222)链接。我使用MKL的顺序版本,禁用OpenMP。问题是它比Matlab慢。
一些示例代码:
#define NDEBUG
#define EIGEN_USE_MKL_ALL
#include <iostream>
#include <chrono>
#include <Core>
using namespace Eigen;
using namespace std;
int main(){
MatrixXd jac = 100*MatrixXd::Random(10*1228, 2850);
MatrixXd res = MatrixXd::Zero(2850, 2850);
for (int i=0; i<10; i++){
auto begin = chrono::high_resolution_clock::now();
res.noalias() = jac.transpose()*jac;
auto end = chrono::high_resolution_clock::now();
cout<<"time: "<<chrono::duration_cast<chrono::milliseconds>(end-begin).count() <<endl;
}
return 0;
}
平均报告时间约为8秒。使用Ubuntu 16.04上的g++ 6.4编译时,采用-O3和无调试符号。
Matlab代码:
m=100*(-1+2*rand(10*1228, 2850));
res = zeros(2850, 2850);
tic; res=m'*m; toc
它报告大约4秒,比原来快了两倍。我在同一系统上使用的是maxNumCompThreads(1)的Matlab R2017a版本。Matlab使用MKL 11.3。
如果没有MKL,只使用Eigen,则需要大约18秒。有什么方法可以将C++运行时间降到与Matlab相同的值?
谢谢。
后续编辑: 正如@Qubit所建议的那样,Matlab认识到我正在尝试将矩阵乘以它的转置,并进行了一些“隐藏”的优化。当我在Matlab中相乘两个不同的矩阵时,时间增加到了8秒。
所以,现在问题变成了:如何告诉Eigen这个矩阵乘积是“特殊的”并且可以进一步优化?
后续编辑2: 我尝试像这样做:
MatrixXd jac = 100*MatrixXd::Random(10*1228, 2850);
MatrixXd res = MatrixXd::Zero(2850, 2850);
auto begin = chrono::high_resolution_clock::now();
res.selfadjointView<Lower>().rankUpdate(jac.transpose(), 1);
res.triangularView<Upper>() = res.transpose();
auto end = chrono::high_resolution_clock::now();
MatrixXd oldSchool = jac.transpose()*jac;
if (oldSchool.isApprox(res)){
cout<<"same result!"<<endl;
}
cout<<"time: "<<chrono::duration_cast<chrono::milliseconds>(end-begin).count() <<endl;
但现在它需要 9.4 秒(这是没有 MKL 的 Eigen 进行经典乘积所需时间的一半)。禁用 MKL 不会对此计时产生时间影响,因此我认为“rankUpdate”方法不使用 MKL ?!?
最后编辑: 我在 eigen 头文件中发现了一个 bug:
Core/products/GeneralMatrixMatrixTriangular_BLAS.h
在第55行有一个括号放错了位置。我进行了更改:
if ( lhs==rhs && ((UpLo&(Lower|Upper)==UpLo)) ) { \
转换为:
if ( lhs==rhs && ((UpLo&(Lower|Upper))==UpLo) ) { \
现在,我的C++版本和Matlab具有相同的执行速度(在我的系统上约为4秒)。