简体   繁体   English

使用mexCallMATLAB的matlab mex文件比相应的m文件慢近300倍

[英]Matlab mex-file with mexCallMATLAB is almost 300 times slower than the corresponding m-file

I started implementing a few m-files in C++ in order to reduce run times. 我开始用C ++实现一些m文件,以减少运行时间。 The m-files produce n-dimensional points and evaluate function values at these points. m文件产生n维点并在这些点评估函数值。 The functions are user-defined and they are passed to m-files and mex-files as function handles. 这些函数是用户定义的,它们作为函数句柄传递给m文件和mex文件。 The mex-files use mexCallMATLAB with feval for finding function values. mex文件使用mexCallMATLAB和feval来查找函数值。

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. 我构造了下面的例子,其中在Matlab命令行中构造的函数句柄fn被传递给matlabcallingmatlab.m和mexcallingmatlab.cpp例程。 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. 使用新开放的Matlab,mexcallingmatlab在241.5秒内评估此功能200000,而matlabcallingmatlab在0.81522秒内评估它,因此使用mex实现减慢296倍。 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. 我尝试了不同的mex编译标志来优化mex,但性能几乎没有差异。 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. Stackoverflow上的一篇文章指出,升级Matlab是解决方案,但我在Mac OS X版本:10.8.4上使用的最新版本MATLAB版本:8.1.0.604(R2013a)。 I did compile the mex file with and without –largeArrayDims flag but this didn't make any difference either. 我使用和不使用-largeArrayDims标志编译了mex文件,但这也没有任何区别。 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. 有人建议函数句柄的内容可以直接在cpp文件中编码,但这是不可能的,因为我想将此代码提供给具有矢量输入和实数输出的任何类型函数的任何用户。

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. 据我所知,mex文件需要通过feval函数来使用函数句柄,而m文件可以直接调用函数句柄,前提是Matlab版本比某些版本更新。

Any help would be greatly appreciated. 任何帮助将不胜感激。

simple function handle created in the Matlab command line : 在Matlab命令行中创建的简单函数句柄

fn = @(x) x'*x 

matlabcallingmatlab.m : matlabcallingmatlab.m

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

mexcallingmatlab.cpp : 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文件

>> 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 . 基本上,你的代码有,你没有释放的一个小的内存泄漏lhs mxArray从调用返回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内存管理器在MEX文件退出时负责释放内存:

MATLAB allocates dynamic memory to store the mxArrays in plhs . MATLAB分配动态内存以在mxArrays中存储plhs MATLAB automatically deallocates the dynamic memory when you clear the MEX-file. 清除MEX文件时,MATLAB会自动释放动态内存。 However, if heap space is at a premium, call mxDestroyArray when you are finished with the mxArrays plhs points to. 但是,如果堆空间非常mxDestroyArray ,请在完成mxArrays plhs指向后调用mxDestroyArray

Still explicit is better than implicit... So your code is really stressing the deallocator of the MATLAB memory manager :) 仍然显式优于隐式...所以你的代码真的强调MATLAB内存管理器的解除分配器:)

mexcallingmatlab.cpp 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 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. 纯MATLAB部分仍然快两倍,可能是因为调用外部MEX函数的开销。

(My system: Win8 running 64-bit R2013a) (我的系统:Win8运行64位R2013a)

There's absolutely no reason to expect that a MEX file is, in general, faster than an M file. 通常,没有理由期望MEX文件比M文件更快。 The only reason that this is often true is that many loops in MATLAB incur a lot of function call overhead, along with parameter checking and such. 这通常是正确的唯一原因是MATLAB中的许多循环会产生大量的函数调用开销,以及参数检查等。 Rewriting that in C eliminates the overhead, and gives your C compiler a chance to optimize the code. 在C中重写可以消除开销,并为您的C编译器提供优化代码的机会。

In this case, there's nothing for the C compiler to optimize... it MUST make the MATLAB interface call for every iteration. 在这种情况下,C编译器没有任何优化...它必须为每次迭代调用MATLAB接口。 In fact, the MATLAB optimizer will do a better job, since it can, in some cases "see" into the function. 实际上,MATLAB优化器会做得更好,因为在某些情况下它可以“​​看到”函数。

In other words, forget using MEX to speed up this program. 换句话说,忘记使用MEX来加速这个程序。

There is some overhead cost in calls from mex to Matlab and vice versa. 从mex到Matlab的调用有一些开销成本,反之亦然。 The overhead per call is small, but it it really adds up in a tight loop like this. 每次调用的开销很小,但它确实在这样的紧密循环中加起来。 As your testing indicates, pure Matlab can be much faster in this case! 正如您的测试所示,在这种情况下,纯Matlab可以更快! Your other option is to eliminate the mexCallMATLAB call and do everything in pure C++. 您的另一个选择是消除mexCallMATLAB调用并在纯C ++中执行所有操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM