【问题标题】:Avoid copying an array when using mexCallMATLAB使用 mexCallMATLAB 时避免复制数组
【发布时间】:2014-12-24 13:02:18
【问题描述】:

我为 MATLAB 编写了一个 mex 文件。它调用 MATLABpinv 函数来计算 Moore Penrose 伪逆。我将此函数命名为my_pinvmy_pinv 获取一个数组并返回它的伪逆,与pinv 完全相同:

A = magic(8); A = A(:,1:6)
b = 260*ones(8,1)
x = my_pinv(A)*b

然而,在 mex 文件中,我必须复制输入数组的值才能使用mexCallMATLAB。这里是my_pinv.cpp的内容:

#include <matrix.h>
#include <mex.h>
#include <string.h>

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
    #define PRHS_A prhs[0]
    #define PLHS_X plhs[0] 

    int M = mxGetM( PRHS_A ); // Get the dimensions of  A.
    int N = mxGetN( PRHS_A );

    double *A_ptr = mxGetPr( PRHS_A );
    mxArray *PINV_A =  mxCreateDoubleMatrix(M, N,  mxREAL);  /* Put input in an mxArray */
    memcpy(mxGetPr(PINV_A), A_ptr,  sizeof(double)*M*N);

    PLHS_X = mxCreateDoubleMatrix(N, M, mxREAL);  // Create the output matrix.

    mexCallMATLAB(1, &PLHS_X, 1, &PINV_A, "pinv");

}

有没有我跳过使用memcpy 并直接使用mexCallMATLAB 中的输入数组prhs[0]?我实际上不喜欢需要复制输入数组的值的事实,尤其是当输入数组非常大时。

事实上,我希望能够使用类似的东西

mexCallMATLAB(1, &PLHS_X, 1, &RHS_A, "pinv"); // (I know it is not right and the compiler would not like it but it is for the sake of example)

而不是

mexCallMATLAB(1, &PLHS_X, 1, &PINV_A, "pinv");

有人可以分享他/她在这方面的经验吗?

【问题讨论】:

    标签: arrays matlab mex memcpy


    【解决方案1】:

    mexCallMATLAB 具有以下签名:

    int mexCallMATLAB(int nlhs, mxArray *plhs[], int nrhs,
        mxArray *prhs[], const char *functionName);
    

    由于某种原因,RHS 数组未使用const 限定符进行标记。我不知道为什么...这解释了为什么会出现编译错误:

    // this is from Visual C++ 2013
    error C2664: 'int mexCallMATLAB(int,mxArray*[],int,mxArray *[],const char *)' :
    cannot convert argument 4 from 'const mxArray *[]' to 'mxArray *[]'
        Conversion loses qualifiers
    

    解决方案是明确抛弃告诉编译器我们知道我们在做什么的常量:

    my_pinv.cpp

    #include "mex.h"
    
    void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
    {
        // validate arguments
        if(nrhs != 1 || nlhs > 1)
            mexErrMsgIdAndTxt("mex:error", "Wrong number of arguments.");
        //perhaps do more validations here..
    
        // out = pinv(in)
        mexCallMATLAB(1, plhs, 1, const_cast<mxArray**>(prhs), "pinv");
    }
    

    现在在 MATLAB 中:

    >> x = rand(4,3);
    >> my_pinv(x) - pinv(x)
    ans =
         0     0     0     0
         0     0     0     0
         0     0     0     0
    

    如果由于某种原因并且在某些极端情况下这被证明是有问题的(我对此表示怀疑),更安全的方法是使用以下方法复制数组:

    mxArray *in = mxDuplicateArray(prhs[0]);
    mexCallMATLAB(1, plhs, 1, &in, "pinv");
    mxDestroyArray(in);
    

    如果您绝对想避免创建深层副本,undocumented functions 会创建一个共享数据副本(其中只创建一个新的数组标头,但数据是共享的)。

    【讨论】:

    • 3 秒后发布我的。 :) +1 顺便说一句,我怎么能比 15 分钟更频繁地收到通知(实际上更长)?
    • 尚不清楚mexCallMATLAB 是否会释放传入的矩阵。无论如何,您使用const_cast 的版本将与问题中的代码具有不同的使用次数。
    • 是的,mexCallMATLAB 的描述是:“MATLAB allocates dynamic memory to store the mxArrays in plhs. MATLAB automatically deallocates the dynamic memory when you clear the MEX-file”。然而,gatway MEX 函数的plhs(不要与mexCallMATLABplhs 参数混淆)是一种特殊情况,如果将数组分配给它,它就会从要处理的事物列表中删除自动释放。这没有完全记录在案,但我在某处读过它(很可能来自 James Tursa 的帖子)
    【解决方案2】:

    抛弃 const 以将 prhs 直接输入到 mexCallMATLAB,正如 Amro 已经说过的那样,如何最有效地处理这个问题。虽然mexCallMATLAB 的声明当然不能保证输入数组不会被修改(声明为mxArray *prhs[]),但如果您调用的函数表现良好并遵循 MathWorks 的严厉建议,则永远不要修改右侧参数,这不是问题。

    关于mexCallMATLABplhs 的分配,这与任何其他 MATLAB 函数一样。如果你这样做没有区别:

    % in MATLAB
    x = pinv(A);
    

    % in MEX
    mxArray *x;  % no mxArray allocated, just a pointer
    mexCallMATLAB(1, &x, 1, const_cast<mxArray**>(prhs), "pinv");  % pinv creates *x
    

    mexCallMATLAB(或者更确切地说是pinv)创建输出数组。 不要浪费时间创建左侧。如果这让您感到困扰,请创建一个空的mxArrary

    mxArray *x = mxCreateDoubleMatrix(0, 0, mxREAL); % but not necessary
    

    pinv 将清除它并创建一个新的,就像您在 MATLAB 中执行此操作一样:

    x = [];
    x = pinv(A);
    

    无论哪种方式,MATLAB 内存管理器都拥有该数组。当 MEX 函数返回或从内存中清除时,它不会消失。

    来自 MathWorks 的 Memory Management Issues

    当 MEX 文件将控制权返回给 MATLAB® 时,它会在输出参数(左侧参数 plhs[] 中包含的 mxArrays)中返回其计算结果。 MATLAB 销毁由 MEX 文件创建的不在此参数列表中的任何 mxArray。

    但是,不要破坏源 MEX 文件中的 mxArray:
    * 传递到右侧列表中的 MEX 文件 prhs[]
    * 在左侧列表中返回 plhs[]
    * 由 mexGetVariablePtr
    返回 * 用于创建结构

    【讨论】:

    • +1 啊,很好的发现,毕竟有文档记录(mexCallMATLAB 分配的plhs 在退出时不会被释放):)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-23
    • 1970-01-01
    • 2013-04-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多