【问题标题】:Matlab mex-file with mexCallMATLAB is almost 300 times slower than the corresponding m-file带有 mexCallMATLAB 的 Matlab mex 文件几乎比相应的 m 文件慢 300 倍
【发布时间】:2013-09-10 17:03:36
【问题描述】:

我开始在 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 是解决方案,但我在 Mac OS X 版本:10.8.4 上使用的可能是最新版本的 MATLAB 版本:8.1.0.604 (R2013a)。我确实编译了带有和不带有 –largeArrayDims 标志的 mex 文件,但这也没有任何区别。有人建议函数句柄的内容可以直接编码在 cpp 文件中,但这是不可能的,因为我想将此代码提供给任何具有向量输入和实数输出的任何类型函数的用户。

据我所知,mex 文件需要通过 feval 函数才能使用函数句柄,而 m-files 可以直接调用函数句柄,前提是 Matlab 版本比某些版本更新。

任何帮助将不胜感激。

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

fn = @(x) x'*x 

ma​​tlabcallingmatlab.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] );
    }
}

mex文件的编译

>> mex -v -largeArrayDims mexcallingmatlab.cpp

【问题讨论】:

  • 那么,您是在使用 C++ 调用执行“x * x”的 Matlab 函数吗?如果 Matlab 比您的 C++ 解决方案做得更好,我不会感到惊讶。因为 Matlab 代码不必跳过所有的环节来将数据从 C++ 转换为 Matlab 格式并返回到 C++ 格式。
  • 你确定你还没有测量过调用mexCallMATLAB 200000 次的开销吗?
  • 如果您想在此处提高性能,请对您的函数进行矢量化处理,使其适用于整个矢量/矩阵,而不是一次处理一个输入。不幸的是,在 MATLAB 中,与其他语言相比,函数调用的开销很高,因此我们的想法是尽量减少评估 fh.. 的次数。例如,可以进行上述操作:x = [zeros(1,199999);1:199999]; fh = @(x) dot(x,x); out = fh(x); 其中 dot 函数(vector dot product) 已经被矢量化并作用于输入参数的列
  • @Amro:如果我是用户,我可以优化功能,但我不能假设其他用户已经仔细优化了功能。
  • @Meteor:虽然其他人说的是真的,但真正的问题是您依赖于 MEX 文件中的内存自动释放,这对于成千上万的mxArrays 来说真的很慢。因此,如果您在通话后明确清理,问题就会消失。请在下面查看我的答案...

标签: c++ performance matlab mex function-handle


【解决方案1】:

所以我尝试自己实现这个,我想我找到了缓慢的原因。

基本上你的代码有一个小的内存泄漏,你没有释放从调用mexCallMATLAB返回的lhsmxArray。这并不完全是内存泄漏,看到 MATLAB 内存管理器会在 MEX 文件退出时释放内存:

MATLAB 分配动态内存以将mxArrays 存储在plhs 中。 当您清除 MEX 文件时,MATLAB 会自动释放动态内存。 但是,如果堆空间非常宝贵,请致电 mxDestroyArray mxArrays plhs 指向你已经完成了。

仍然显式优于隐式...所以您的代码确实在强调 MATLAB 内存管理器的释放器 :)

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))

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

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

与您最初的缓慢时间相差无几!纯 MATLAB 部分的速度仍然大约是原来的两倍,这可能是因为调用外部 MEX 函数的开销。

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

【讨论】:

  • 非常感谢。添加 mxDestroyArray(lhs[0]);我的代码的for循环中的行解决了这个问题。现在我的运行时间几乎相同,都在 0.8 秒左右。这种表现肯定是可以接受的。我也要感谢其他人提供的信息丰富的 cmets。
【解决方案2】:

一般来说,绝对没有理由期望 MEX 文件比 M 文件快。这通常是正确的唯一原因是 MATLAB 中的许多循环会产生大量的函数调用开销,以及参数检查等。用 C 重写可以消除开销,并让 C 编译器有机会优化代码。

在这种情况下,C 编译器无需优化...它必须为每次迭代调用 MATLAB 接口。事实上,MATLAB 优化器会做得更好,因为它在某些情况下可以“看到”函数。

换句话说,忘记使用 MEX 来加速这个程序。

【讨论】:

  • 这段代码只代表整个程序的一小部分。 C 编译器在所有其他部分都做得很好,但这部分是瓶颈。
【解决方案3】:

从 mex 到 Matlab 的调用会产生一些间接费用,反之亦然。每次调用的开销很小,但它确实像这样在一个紧密的循环中加起来。正如您的测试所示,在这种情况下,纯 Matlab 可以更快!您的另一个选择是消除 mexCallMATLAB 调用并使用纯 C++ 执行所有操作。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-25
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    相关资源
    最近更新 更多