gpt4 book ai didi

c++ - 性能:Matlab 与 C++ 矩阵 vector 乘法

转载 作者:可可西里 更新时间:2023-11-01 18:27:52 29 4
gpt4 key购买 nike

序言

前段时间我问了一个关于 Matlab 与 Python 性能的问题 (Performance: Matlab vs Python)。我很惊讶 Matlab 比 Python 更快,尤其是在 meshgrid 方面。在讨论该问题时,有人指出我应该使用 Python 中的包装器来调用我的 C++ 代码,因为我也可以使用 C++ 代码。我在 C++、Matlab 和 Python 中有相同的代码。

在这样做的同时,我再次惊讶地发现 Matlab 在矩阵组装 计算方面比 C++ 更快。我有一个稍微大一点的代码,我正在研究一段矩阵- vector 乘法。较大的代码在多个实例中执行此类乘法。总体而言,C++ 中的代码比 Matlab 快得多(因为 Matlab 中的函数调用有开销等),但 Matlab 在矩阵 vector 乘法方面似乎优于 C++(底部的代码片段)。

结果

下表显示了组装内核矩阵所花费的时间与矩阵与 vector 相乘所花费的时间的比较。结果针对矩阵大小 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);

类(使用 meshgrid):

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_ 是一个 vector ,最好使用 VectorXd 来让 Eigen 知道您需要矩阵- vector 积而不是矩阵-矩阵积。

如果你有Intel的MKL,那么你也可以让Eigen uses it在这种精确操作中获得与 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,而矩阵 vector 乘积需要 0.24s,所以总共需要 0.6s,这比 MatLab 快,而我的 CPU 似乎更慢比你的。

关于c++ - 性能:Matlab 与 C++ 矩阵 vector 乘法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46714235/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com