使用mexCallMATLAB时避免复制数组

时间:2014-10-28 22:55:05

标签: arrays matlab mex memcpy

我为MATLAB编写了一个mex文件。它调用MATLAB pinv函数来计算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并直接使用prhs[0]中的输入数组mexCallMATLAB?我实际上不喜欢输入数组的值需要被复制的事实,特别是当输入数组非常大时。

事实上,我希望能够使用像

这样的东西
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");

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

2 个答案:

答案 0 :(得分:4)

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创建共享数据副本(其中只创建新的数组头,但共享数据)。

答案 1 :(得分:2)

将const转换为prhs直接输入mexCallMATLAB,因为Amro已经说过如何最有效地处理这个问题。虽然mexCallMATLAB的声明肯定不能保证输入数组不会被修改(声明为mxArray *prhs[]),但如果你调用的函数表现良好并遵循MathWorks的严格建议,则永远不要修改右边的论点,这不是问题。

关于plhs mexCallMATLAB的分配,这与任何其他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返回    *用于创建结构