How do I access variables on Fortran from MATLAB using MEX (without copying them)?

91 views Asked by At

I would to ask how I can access a variable on Fortran from MATLAB without making a copy. Preferrably, I would like to access the variable a variable on Fortran (it may be 'read-only') that was declared and initialized on MATLAB and passed as an input to my Fortran-based MEX function as input (pretty much a pass-by-reference), and if possible, do it for a multidimensional array.

The solutions that I have found so far require copying the variable, which is quite costly for large inputs.

So far, I have tried the approach based on the solution given in the MATLAB documentation, where they implement a function in the form y = times2(x) which doubles my double scalar x. However, it requires copying the variable x, so there is the original x declared on MATLAB and another one on Fortran.

For reference, MATLAB recommends first declaring a pointer and its associated variable

mwPointer x_ptr real*8 x_input

And then copying the content of the MATLAB variable x into the Fortran variable x_input via:

call mxCopyPtrToReal8(x_ptr,x_input,size)

Where size is 1, as x is a scalar, but in my application I will eventually use a multi-dimensional array.

This solution is unappealing when one needs to operate on large arrays, as they would be copied, doubling my memory requirements. I have found this function mexGetVariablePtr which returns a read-only MATLAB pointer that theoretically should give me access to the variable x without copying it.

My question is: how do I access this variable, as the only function that I found in the API documentation, mxCopyPtrToReal, copies the variable.

Additional information:

  • I only want to read the contents of the variable 'x' and use as a normal Fortran variable without modifying it.
  • The output of 'mexGetVariablePtr' is a 'mwPointer', which seems to not be easy to dereference like regular Fortran pointers.

Thank you for your attention!

2

There are 2 answers

0
Vinicius Oliveira Fontes On

I found the solution in a old source.

To those interested, basically you need to declare the appropriate functions as integer, not mwPointer (which is shown in MATLAB's documentation).

The (non-read only) pointer of a is then converted to an integer via:

pr_in = mxGetPr(prhs(1))

Lastly, you send the input variable as a dynamic allocated one with

call compute(%val(pr_out), %val(pr_in), size)

using the operator %.

Finally, you will be able to use the variable in the function compute without making a copy. If you change the content of an input variable it will be changed everywhere.

The only problem with this approach is that I do not know how to send a multidimensional array without using reshape, which would require me to make a temporary copy of the variable.

For further information, see 1.

0
James Tursa On

In addition to the %val( ) approach, there is a MATLAB FEX contribution that I wrote that creates Fortran pointers to the data area of MATLAB double variables with or without making a deep copy (your choice). So you can have the full Fortran syntax available to you (multi-dimensional indexing and slicing, passing conforming subarrays to functions using implicit or explicit interfaces, dynamically allocating Fortran pointers using MATLAB mxMalloc in the background so memory is on MATLAB garbage collection list, etc.). It also has functionality to reshape the Fortran pointers without making deep copies. And it has functionality to make deep copies also if that is what you want. And functionality to turn Fortran arrays or pointers into mxArray variables. The FEX contribution hides all of the %val( ) and mxMalloc and mxFree stuff in the background. You can find it here:

https://www.mathworks.com/matlabcentral/fileexchange/25934-fortran-95-interface-to-matlab-api-with-extras

That being said, it was written some time ago and for R2018a and later it only works for real arrays, not complex arrays, because MATLAB changed from separate real-complex data areas to interleaved real-complex data areas in 2018. I am working on an update for the interleaved complex memory model (I just recently installed a Fortran compiler compatible with my current version of MATLAB), but the update probably won't be ready for another month or two because I have to completely rewrite major portions of the code, do testing, etc.

Some background on mex interfaces in general (applies to C and Fortran):

  • There are NO official API functions available to tell if an input variable is shared or not. The only way to tell this is to hack into the mxArray and look at the reference copy counter and the shared data copy pointer. If either of those is non-zero, then the variable is shared. If these are both 0, then you need to look at the type of the variable. If it is an element of a cell or struct, then you don't know the sharing status and can't find out because the variable might be shared through a parent and you can't backtrack this in a mex routine. Bottom line is you can never really know for sure, even with hacking, if a variable is not shared with anything else. So unless you are very, very, careful at the m-file level to not do anything that would cause a variable to be shared (and this can be very tricky because the rules for this are undocumented and change from release to release), don't rely on that in a mex routine.
  • Older versions of MATLAB used to pass the original variable pointers into the mex routine. Later versions of MATLAB create temporary shared data copies of the input variables and pass those pointers into the mex routine.
  • If you compile a mex routine without the -R2018a option in R2018a or later, MATLAB will deep copy all complex variables into and out of the mex routine so that it will be compatible with the older interface with separate real-complex data areas and mxGetPr and mxGetPi work as expected.
  • If you compile a mex routine with the -R2018a option in R2018a or later, you won't get deep copies of complex variables in & out, but older syntax like mxGetPi of course will not work (there is no pi pointer in the mxArray).
  • If the input is a classdef object, the only official way to get at the properties is with the mxGetProperty and mxSetProperty functions, and these only work with deep copies in both directions. There are undocumented methods and hacks to get pointers to the properties (that sometimes work), but that is beyond the scope of this thread. If you are willing to move to their C++ interface I think there may be methods available for shallow pointers to properties, but I am unfamiliar with the C++ interface so can't help you there.