问题
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