性能:Matlab与C++矩阵矢量乘法
Performance: Matlab vs C++ Matrix vector multiplication
前导
不久前,我问了一个关于Matlab与Python性能的问题(性能:Matlab与Python)。我很惊讶Matlab比Python快,尤其是在meshgrid
中。在讨论这个问题时,有人告诉我,我应该使用Python中的包装器来调用我的C++代码,因为我也可以使用C++代码。我在C++、Matlab和Python中都有相同的代码。
在这样做的同时,我再次惊讶地发现,在矩阵汇编和计算方面,Matlab比C++更快。我有一个稍大的代码,我正在研究矩阵向量乘法的一个片段。较大的代码在多个实例中执行这样的乘法运算。总体而言,C++中的代码比Matlab快得多(因为Matlab中的函数调用有开销等),但Matlab似乎在矩阵向量乘法方面优于C++(底部的代码片段)。
结果
下表显示了组装内核矩阵所需的时间与将矩阵与向量相乘所需时间的比较。针对矩阵大小CCD_ 2编译结果,其中CCD_ 3在10000到40000之间变化。没有那么大。但有趣的是,N
越大,Matlab的性能就越优于C++。Matlab的总时间要快3.8-5.8倍。此外,它在矩阵组装和计算中也更快。
___________________________________________
|N=10,000 Assembly Computation Total |
|MATLAB 0.3387 0.031 0.3697 |
|C++ 1.15 0.24 1.4 |
|Times faster 3.8 |
___________________________________________
|N=20,000 Assembly Computation Total |
|MATLAB 1.089 0.0977 1.187 |
|C++ 5.1 1.03 6.13 |
|Times faster 5.2 |
___________________________________________
|N=40,000 Assembly Computation Total |
|MATLAB 4.31 0.348 4.655 |
|C++ 23.25 3.91 27.16 |
|Times faster 5.8 |
-------------------------------------------
问题
在C++中有更快的方法吗?我是不是错过了什么?我知道C++正在使用for
循环,但我的理解是Matlab也将在meshgrid
中做类似的事情。
代码段
Matlab代码:
%% GET INPUT DATA FROM DATA FILES ------------------------------------------- %
% Read data from input file
Data = load('Input/input.txt');
location = Data(:,1:2);
charges = Data(:,3:end);
N = length(location);
m = size(charges,2);
%% EXACT MATRIX VECTOR PRODUCT ---------------------------------------------- %
kex1=ex1;
tic
Q = kex1.kernel_2D(location , location);
fprintf('n Assembly time: %f ', toc);
tic
potential_exact = Q * charges;
fprintf('n Computation time: %f n', toc);
类别(使用网格):
classdef ex1
methods
function [kernel] = kernel_2D(obj, x,y)
[i1,j1] = meshgrid(y(:,1),x(:,1));
[i2,j2] = meshgrid(y(:,2),x(:,2));
kernel = sqrt( (i1 - j1) .^ 2 + (i2 - j2) .^2 );
end
end
end
C++代码:
编辑
使用带有以下标志的make文件编译:
CC=g++
CFLAGS=-c -fopenmp -w -Wall -DNDEBUG -O3 -march=native -ffast-math -ffinite-math-only -I header/ -I /usr/include
LDFLAGS= -g -fopenmp
LIB_PATH=
SOURCESTEXT= src/read_Location_Charges.cpp
SOURCESF=examples/matvec.cpp
OBJECTSF= $(SOURCESF:.cpp=.o) $(SOURCESTEXT:.cpp=.o)
EXECUTABLEF=./exec/mykernel
mykernel: $(SOURCESF) $(SOURCESTEXT) $(EXECUTABLEF)
$(EXECUTABLEF): $(OBJECTSF)
$(CC) $(LDFLAGS) $(KERNEL) $(INDEX) $(OBJECTSF) -o $@ $(LIB_PATH)
.cpp.o:
$(CC) $(CFLAGS) $(KERNEL) $(INDEX) $< -o $@
`
# include"environment.hpp"
using namespace std;
using namespace Eigen;
class ex1
{
public:
void kernel_2D(const unsigned long M, double*& x, const unsigned long N, double*& y, MatrixXd& kernel) {
kernel = MatrixXd::Zero(M,N);
for(unsigned long i=0;i<M;++i) {
for(unsigned long j=0;j<N;++j) {
double X = (x[0*N+i] - y[0*N+j]) ;
double Y = (x[1*N+i] - y[1*N+j]) ;
kernel(i,j) = sqrt((X*X) + (Y*Y));
}
}
}
};
int main()
{
/* Input ----------------------------------------------------------------------------- */
unsigned long N = 40000; unsigned m=1;
double* charges; double* location;
charges = new double[N * m](); location = new double[N * 2]();
clock_t start; clock_t end;
double exactAssemblyTime; double exactComputationTime;
read_Location_Charges ("input/test_input.txt", N, location, m, charges);
MatrixXd charges_ = Map<MatrixXd>(charges, N, m);
MatrixXd Q;
ex1 Kex1;
/* Process ------------------------------------------------------------------------ */
// Matrix assembly
start = clock();
Kex1.kernel_2D(N, location, N, location, Q);
end = clock();
exactAssemblyTime = double(end-start)/double(CLOCKS_PER_SEC);
//Computation
start = clock();
MatrixXd QH = Q * charges_;
end = clock();
exactComputationTime = double(end-start)/double(CLOCKS_PER_SEC);
cout << endl << "Assembly time: " << exactAssemblyTime << endl;
cout << endl << "Computation time: " << exactComputationTime << endl;
// Clean up
delete []charges;
delete []location;
return 0;
}
正如评论中所说,MatLab依赖英特尔的MKL矩阵产品库,这是执行此类操作最快的库。尽管如此,Eigen本身应该能够提供类似的性能。为此,请确保使用最新的Eigen(例如3.4)和适当的编译标志来启用AVX/FMA(如果可用)和多线程:
-O3 -DNDEBUG -march=native
由于charges_
是一个向量,最好使用VectorXd
来Eigen知道你想要的是矩阵向量乘积,而不是矩阵矩阵乘积。
如果你有英特尔的MKL,那么你也可以让Eigen使用它来获得与MatLab完全相同的性能。
关于汇编,最好反转两个循环以启用矢量化,然后使用OpenMP启用多线程(添加-fopenmp
作为编译器标志)以使最外层的循环并行运行,最后您可以使用Eigen简化代码:
void kernel_2D(const unsigned long M, double* x, const unsigned long N, double* y, MatrixXd& kernel) {
kernel.resize(M,N);
auto x0 = ArrayXd::Map(x,M);
auto x1 = ArrayXd::Map(x+M,M);
auto y0 = ArrayXd::Map(y,N);
auto y1 = ArrayXd::Map(y+N,N);
#pragma omp parallel for
for(unsigned long j=0;j<N;++j)
kernel.col(j) = sqrt((x0-y0(j)).abs2() + (x1-y1(j)).abs2());
}
使用多线程,您需要测量墙上的时钟时间。在这里(Haswell有4个物理内核,运行频率为2.6GHz),当N=20000时,组装时间下降到0.36s,矩阵矢量产品总共需要0.24s,所以总共需要0.6s,这比MatLab快,而我的CPU似乎比你的慢。
您可能有兴趣查看MATLAB Central的贡献mtimesx。
Mtimesx是一个mex函数,它使用BLAS库、openMP和其他方法优化矩阵乘法。根据我的经验,当它最初发布时,在某些情况下可能会比股票MATLAB高出3个数量级。(我想,这对MATHWORKS来说有点尴尬。)这些天,MATLAB改进了自己的方法(我怀疑是借鉴了这一点。)并且差异没有那么严重。MATLAB有时比它执行得更好。
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 有可能在Armadillo中复制MATLAB circshift方法吗
- 使用 MATLAB 编码器生成C++代码:编译错误"undefined reference to `rgb2gray_tbb_real64'"
- MATLAB to C++: csvread() not supported by MATLAB Coder
- 加载由 MATLAB Coder 生成的带有函数的 DLL,该函数调用外部函数
- MATLAB:跟踪imufilter对象中的状态变化
- 如何将 c++ 中的客户端 TCP 的替身列表发送到 Matlab 中的 TCP 服务器?
- 将三角函数的正确值与matlab进行比较
- 连接 MATLAB 和 Visual Studios 的问题
- 如何将C++连接到 Matlab
- 如何发送 Mat H=findHomography 返回 Matlab
- 如何在C++中编写 MATLAB fix(X) 函数?
- 提供变量作为 MATLAB 系统命令的输入参数,以便C++可执行文件
- 有没有更好的方法可以使用特征/C++实现 matlab 的逻辑索引?
- 当我运行MEX文件时,MATLAB崩溃
- 从 MATLAB 到 C++:相当于带有选项 'remove' 的 bwmorph
- MATLAB的imfinfo in openCV
- 将C++中的多个参数传递给MatLab共享库函数
- 将 matlab 数组(MAT-文件)转换为C++数组
- 数组数据以错误的方式遍历 Python/Matlab