Avoid copying an array when using mexCallMATLAB

只谈情不闲聊 提交于 2019-12-12 14:43:34

问题


I have written a mex file for MATLAB. It calls MATLAB pinv function to calculate the Moore Penrose pseudoinverse. I named this function my_pinv. my_pinv gets an array and returns its pseudoinverse, exactly similar to pinv:

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

In the mex file, however, I have to copy the values of the input array to be able to use mexCallMATLAB. Here is the content of 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");

}

Is there anyway that I skip using memcpy and directly use the input array, prhs[0], in mexCallMATLAB? I actually don't like the fact that the values of input array needs to be copied especially when the input array is very large.

In fact, I would like to be able to use something like

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)

rather than

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

Could someone share his/her experiences in this regard?


回答1:


mexCallMATLAB has the following signature:

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

For some reason, the RHS array is not marked with the const qualifier. I dont know why... This explains why you get a compilation error:

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

The solution is to explicitly cast away the constant-ness telling the compiler that we know what we're doing:

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");
}

Now in MATLAB:

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

If for some reason and in some corner case this proves problematic (I doubt it), the safer way is to just duplicate the array using:

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

If you absolutely want to avoid creating a deep copy, there are undocumented functions that create a shared-data copy (where only a new array header is created, but the data is shared).




回答2:


Casting away the const to input prhs directly to mexCallMATLAB as Amro already said is how to handle this most efficiently. Although the declaration for mexCallMATLAB certainly does not guarantee that the input array will not be modified (declared as mxArray *prhs[]), if the function you call is well behaved and follows the stern advice of MathWorks never to modify the right hand side arguments, this is not a problem.

Regarding the allocation of plhs by mexCallMATLAB, this is done just like any other MATLAB function. There is no difference if you do:

% in MATLAB
x = pinv(A);

or

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

mexCallMATLAB (or rather pinv) creates the output array. Don't waste time creating the left hand side. If this bothers you, create an empty mxArrary:

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

pinv will wipe it out and make a new one, just as if you had done this in MATLAB:

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

Either way, the MATLAB memory manager owns the array. It will not evaporate when the MEX function returns or is cleared from memory.

From Memory Management Issues at MathWorks:

When a MEX-file returns control to MATLAB®, it returns the results of its computations in the output arguments—the mxArrays contained in the left-side arguments plhs[]. MATLAB destroys any mxArray created by the MEX-file that is not in this argument list.

However, do not destroy an mxArray in a source MEX-file when it is:
* passed to the MEX-file in the right-hand side list prhs[]
* returned in the left side list plhs[]
* returned by mexGetVariablePtr
* used to create a structure



来源:https://stackoverflow.com/questions/26619947/avoid-copying-an-array-when-using-mexcallmatlab

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!