【发布时间】:2018-03-24 15:56:46
【问题描述】:
序言
前段时间我问了一个关于 Matlab 与 Python 性能的问题 (Performance: Matlab vs Python)。我很惊讶 Matlab 比 Python 快,尤其是在 meshgrid 中。在讨论该问题时,有人指出我应该在 Python 中使用包装器来调用我的 C++ 代码,因为我也可以使用 C++ 代码。我在 C++、Matlab 和 Python 中有相同的代码。
在这样做的同时,我再次惊讶地发现,Matlab 在矩阵汇编和计算方面比 C++ 更快。我有一个稍大的代码,我正在研究一段矩阵-向量乘法。较大的代码在多个实例中执行这样的乘法。总体而言,C++ 中的代码比 Matlab 快得多(因为在 Matlab 中调用函数有开销等),但在矩阵向量乘法方面,Matlab 似乎优于 C++(底部的代码 sn-p)。
结果
下表显示了组装内核矩阵所需的时间以及将矩阵与向量相乘所需的时间的比较。结果针对矩阵大小 NxN 进行编译,其中 N 从 10,000 到 40,000 不等。这不是那么大。但有趣的是,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;
}
【问题讨论】:
-
您应该放置用于编译 C++ 代码的标志
-
您初始化矩阵的方式可以明显得到改进。首先,不要调用 ::Zero,你是在浪费时间初始化一切。其次,尝试查看矩阵是按行优先还是列优先顺序存储的。如果它是列主要的,则内部循环应该在每一行上进行迭代!
-
另请注意:
-ffast-math≠"accurate-math",MATLAB 不会做出妥协 -
如前所述,Matlab 在 BLAS 下使用专门的子程序,这些子程序针对矩阵/向量运算进行了高度优化。要真正比较使用 C++ 的性能,您可以尝试使用 Armadillo,它是用 C++ 编写并使用 BLAS。
标签: c++ matlab performance matrix eigen