Matlab mex文件与mexCallMATLAB几乎是相应的m文件慢300倍 [英] Matlab mex-file with mexCallMATLAB is almost 300 times slower than the corresponding m-file

查看:314
本文介绍了Matlab mex文件与mexCallMATLAB几乎是相应的m文件慢300倍的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始在C ++中实现一些m文件,以减少运行时间。 m文件产生n维点并在这些点评估函数值。这些函数是用户定义的,它们作为函数句柄传递给m文件和mex文件。 mex文件使用mexCallMATLAB和feval来查找函数值。



我构造了下面的例子,其中在Matlab命令行中构造的函数句柄fn被传递到matlabcallingmatlab.m和mexcallingmatlab.cpp例程。使用新打开的Matlab,mexcallingmatlab在241.5秒内评估此函数200000,而matlabcallingmatlab在0.81522秒内评估它,因此与mex实现相比,减慢了296倍。这些时间是第二次运行的结果,因为第一次运行似乎更大,可能是由于一些开销相关的第一次加载程序等。



我花了很多天在线搜索这个问题,并尝试了一些建议。我尝试不同的mex编译标志优化mex,但性能几乎没有差别。在Stackoverflow的上一篇文章说,升级Matlab是解决方案,但我使用大概是最新版本MATLAB版本:8.1.0.604(R2013a)在Mac OS X版本:10.8.4。我没有编译mex文件与和没有-largeArrayDims标志,但这没有什么区别。一些人建议,函数句柄的内容可以直接在cpp文件中编码,但这是不可能的,因为我想提供这个代码给任何类型的函数具有向量输入和实数输出的任何用户。



据我发现,mex文件需要通过feval函数使用一个函数句柄,而m文件可以直接调用函数句柄,如果Matlab版本比一些版本更新。



任何帮助将非常感激。



在Matlab命令行中创建简单的函数句柄

  fn = @(x)x'* x 
/ pre>

matlabcallingmatlab.m

  function matlabcallingmatlab(fn)
x = zeros(2,1);
for i = 0:199999
x(2)= i;
f = fn(x);
end

mexcallingmatlab.cpp

  #includemex.h
#include< cstring>

void mexFunction(int nlhs,mxArray * plhs [],
int nrhs,const mxArray * prhs [])
{
mxArray * lhs [1] * rhs [2]; //传递给feval的参数
double f,* xptr,x [] = {0.0,0.0}; // x:输入到f和f = f(x)
int n = 2,nbytes = n * sizeof(double); // n:维的输入x到f

// prhs [0]是函数句柄作为feval的第一个参数
rhs [0] = const_cast< mxArray *>(prhs [0]);

// rhs [1]包含函数的输入x
rhs [1] = mxCreateDoubleMatrix(n,1,mxREAL);
xptr = mxGetPr(rhs [1]);

for(int i = 0; i <200000; ++ i)
{
x [1] = double(i) // change input
memcpy(xptr,x,nbytes); // now rhs [1] has new x
mexCallMATLAB(1,lhs,2,rhs,feval);
f = * mxGetPr(lhs [0]);
}
}

mex文件的编译

 >> mex -v -largeArrayDims mexcallingmatlab.cpp 


解决方案



基本上你的代码有一个小的内存泄漏,你不能释放 lhs mxArray .html> mexCallMATLAB 。它不完全是一个内存泄漏,看到MATLAB内存管理器照顾MEX文件退出时释放内存:


MATLAB分配动态内存以将 mxArrays 存储在 plhs 中。
当您清除MEX文件时,MATLAB自动释放动态内存。
然而,如果堆空间是溢价,当
时,调用 mxDestroyArray 您完成了 mxArrays plhs 指向。


因此,您的代码真的强调MATLAB内存管理器的释放器:)



mexcallingmatlab.cpp



  #includemex.h

#ifndef N
#define N 100
#endif

mexFunction(int nlhs,mxArray * plhs [],int nrhs,const mxArray * prhs [])
{
//验证输入/输出参数
if(nrhs!= 1){
mexErrMsgTxt(需要一个输入参数。
}
if(mxGetClassID(prhs [0])!= mxFUNCTION_CLASS){
mexErrMsgTxt(Input must be a function handle。
}
if(nlhs> 1){
mexErrMsgTxt(Too many output arguments。
}

//分配输出
plhs [0] = mxCreateDoubleMatrix(N,1,mxREAL);
double * out = mxGetPr(plhs [0]);

//为mexCallMATLAB准备:val = feval(@fh,zeros(2,1))
mxArray * lhs,* rhs [2]
rhs [0] = mxDuplicateArray(prhs [0]);
rhs [1] = mxCreateDoubleMatrix(2,1,mxREAL);
double * xptr = mxGetPr(rhs [1])+ 1;

for(int i = 0; i * xptr = i;
mexCallMATLAB(1,& lhs,2,rhs,feval);
out [i] = * mxGetPr(lhs);
mxDestroyArray(lhs);
}

// cleanup
mxDestroyArray(rhs [0]);
mxDestroyArray(rhs [1]);
}



MATLAB



  fh = @(x)x'* x; 
N = 2e5;

%MATLAB
tic
out = zeros(N,1);
for i = 0:N-1
out(i + 1)= feval(fh,[0; i]);
end
toc

%MEX
mex(' - largeArrayDims',sprintf(' - DN =%d',N),'mexcallingmatlab.cpp')
tic
out2 = mexcallingmatlab(fh);
toc

%检查结果
assert(isequal(out,out2))

运行上面的基准测试几次(预热),我得到以下一致的结果:

 已用时间为0.732890秒。 %pure MATLAB 
经过的时间为1.621439秒。 %MEX-file

没有你最初拥有的慢时间!仍然纯MATLAB部分是快两倍,可能是因为调用一个外部MEX函数的开销。



(我的系统:Win8运行64- bit R2013a)


I started implementing a few m-files in C++ in order to reduce run times. The m-files produce n-dimensional points and evaluate function values at these points. The functions are user-defined and they are passed to m-files and mex-files as function handles. The mex-files use mexCallMATLAB with feval for finding function values.

I constructed the below example where a function handle fn constructed in the Matlab command line is passed to matlabcallingmatlab.m and mexcallingmatlab.cpp routines. With a freshly opened Matlab, mexcallingmatlab evaluates this function 200000 in 241.5 seconds while matlabcallingmatlab evaluates it in 0.81522 seconds therefore a 296 times slow-down with the mex implementation. These times are the results of the second runs as the first runs seem to be larger probably due to some overhead associated first time loading the program etc.

I have spent many days searching online on this problem and tried some suggestions on it. I tried different mex compiling flags to optimize the mex but there was almost no difference in performance. A previous post in Stackoverflow stated that upgrading Matlab was the solution but I am using probably the latest version MATLAB Version: 8.1.0.604 (R2013a) on Mac OS X Version: 10.8.4. I did compile the mex file with and without –largeArrayDims flag but this didn’t make any difference either. Some suggested that the content of the function handle could be directly coded in the cpp file but this is impossible as I would like to provide this code to any user with any type of function with a vector input and real number output.

As far as I found out, mex files need to go through feval function for using a function handle whereas m-files can directly call function handles provided that Matlab version is newer than some version.

Any help would be greatly appreciated.

simple function handle created in the Matlab command line:

fn = @(x) x'*x 

matlabcallingmatlab.m :

function matlabcallingmatlab( fn )
x = zeros(2,1); 
for i = 0 : 199999
    x(2) = i; 
    f = fn( x ); 
end

mexcallingmatlab.cpp:

#include "mex.h"
#include <cstring>

void mexFunction( int nlhs, mxArray *plhs[],
                  int nrhs, const mxArray *prhs[] )
{
    mxArray *lhs[1], *rhs[2]; //parameters to be passed to feval
    double f, *xptr, x[] = {0.0, 0.0}; // x: input to f and f=f(x)
    int n = 2, nbytes = n * sizeof(double);  // n: dimension of input x to f

    // prhs[0] is the function handle as first argument to feval
    rhs[0] = const_cast<mxArray *>( prhs[0] );

    // rhs[1] contains input x to the function
    rhs[1] = mxCreateDoubleMatrix( n, 1, mxREAL);
    xptr = mxGetPr( rhs[1] );

    for (int i = 0; i < 200000; ++i)
    {
        x[1] = double(i);   // change input 
        memcpy( xptr, x, nbytes );  // now rhs[1] has new x
        mexCallMATLAB(1, lhs, 2, rhs, "feval");
        f = *mxGetPr( lhs[0] );
    }
}

Compilation of mex file:

>> mex -v -largeArrayDims mexcallingmatlab.cpp

解决方案

So I tried to implement this myself, and I think I found the reason for the slowness.

Basically your code have a small memory leak where you are not freeing the lhs mxArray returned from the call to mexCallMATLAB. It is not exactly a memory-leak, seeing that MATLAB memory manager takes care of freeing the memory when the MEX-file exits:

MATLAB allocates dynamic memory to store the mxArrays in plhs. MATLAB automatically deallocates the dynamic memory when you clear the MEX-file. However, if heap space is at a premium, call mxDestroyArray when you are finished with the mxArrays plhs points to.

Still explicit is better than implicit... So your code is really stressing the deallocator of the MATLAB memory manager :)

mexcallingmatlab.cpp

#include "mex.h"

#ifndef N
#define N 100
#endif

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    // validate input/output arguments
    if (nrhs != 1) {
        mexErrMsgTxt("One input argument required.");
    }
    if (mxGetClassID(prhs[0]) != mxFUNCTION_CLASS) {
        mexErrMsgTxt("Input must be a function handle.");
    }
    if (nlhs > 1) {
        mexErrMsgTxt("Too many output arguments.");
    }

    // allocate output
    plhs[0] = mxCreateDoubleMatrix(N, 1, mxREAL);
    double *out = mxGetPr(plhs[0]);

    // prepare for mexCallMATLAB: val = feval(@fh, zeros(2,1))
    mxArray *lhs, *rhs[2];
    rhs[0] = mxDuplicateArray(prhs[0]);
    rhs[1] = mxCreateDoubleMatrix(2, 1, mxREAL);
    double *xptr = mxGetPr(rhs[1]) + 1;

    for (int i=0; i<N; ++i) {
        *xptr = i;
        mexCallMATLAB(1, &lhs, 2, rhs, "feval");
        out[i] = *mxGetPr(lhs);
        mxDestroyArray(lhs);
    }

    // cleanup
    mxDestroyArray(rhs[0]);
    mxDestroyArray(rhs[1]);
}

MATLAB

fh = @(x) x'*x;
N = 2e5;

% MATLAB
tic
out = zeros(N,1);
for i=0:N-1
    out(i+1) = feval(fh, [0;i]);
end
toc

% MEX
mex('-largeArrayDims', sprintf('-DN=%d',N), 'mexcallingmatlab.cpp')
tic
out2 = mexcallingmatlab(fh);
toc

% check results
assert(isequal(out,out2))

Running the above benchmark a couple of times (to warm it up), I get the following consistent results:

Elapsed time is 0.732890 seconds.    % pure MATLAB
Elapsed time is 1.621439 seconds.    % MEX-file

No where near the slow times you initially had! Still the pure MATLAB part is about twice as fast, probably because of the overhead of calling an external MEX-function.

(My system: Win8 running 64-bit R2013a)

这篇关于Matlab mex文件与mexCallMATLAB几乎是相应的m文件慢300倍的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆