Question

I want to wrap functionality in a function, in this case incrementing the first element in a possibly huge matrix:

>> tic; A=zeros(100000000,1); toc;
Elapsed time is 0.324779 seconds.
>> tic; A(1)=A(1)+1; toc; A(1)
Elapsed time is 0.000011 seconds.
ans =
     1

Consider that the increment would be a more complicated, code-intensive, operation. Hence, I decided to wrap it in a function:

function A = speedTest(A)
    A(1)=A(1)+1;
end
>> tic; A=speedTest(A); toc; A(1)
Elapsed time is 0.366090 seconds.
ans =
     2

This strongly suggests that A is copied, however it should not be necessary; that is Matlab can detect that (1) A is overwritten in the same statement, (2) the data of A is not shared with another variable. However, it obviously does not do so.

So a solution would be to do the hack as suggested by the OP here: Working with preallocated arrays in Matlab's mex function

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

#include <iostream>

void mexFunction(int nargout, mxArray *argout[], int nargin, const mxArray *argin[]) {
    mxUnshareArray(const_cast<mxArray *>(argin[0]), true); 
    double *data = (double *)mxGetData(argin[0]);
    data[0] = data[0]+1;
}

However this results in an error:

mexSpeedTest.cpp: In function ‘void mexFunction(int, mxArray**, int, const mxArray**)’:
mexSpeedTest.cpp:7:54: error: ‘mxUnshareArray’ was not declared in this scope

mxUnshareArray(const_cast(argin[0]), true);

I'm assuming mxUnshareArray is removed in Matlab 8.1.0.604 (R2013a); my version. I can also simply remove the line, and this works fast:

>> tic; mexSpeedTest(A); toc; A(1)
Elapsed time is 0.000061 seconds.
ans =
     3
>> 

However it breaks Matlab's rule of having immutable right hand values. Unfortunately, I am not willing to give up on cleaning up my code by wrapping functionality in functions, nor to allow for useless slowdowns. Note that I use the function in a for-loop, where I have already tried to optimize parts with c++ and mex.

One solution could be to work around it via OO, that is:

classdef incrementabledata < handle
    properties
        A=[]
    end
    methods
        function HB=incrementabledata()
            HB.A = zeros(100000000,1);
        end
        function increment(HB)
            HB.A(1)=HB.A(1)+1;
        end
    end
end

Which is faster, but not equally fast as the unwrapped bare increment; I'm assuming the data is not copied. Also, it is a lot of boilerplate code.

tic; AA = incrementabledata(); toc
Elapsed time is 0.005504 seconds.
tic; AA.increment(); toc; AA.A(1)
Elapsed time is 0.003939 seconds.
ans =
     1

My questions boil down to these: (1) Is it possible to do some kind of pass by reference construct in Matlab, or to edit and pass an input argument to the output without it being copied? (2) Why is the mxUnshareArray not there? and (3) If I use mxUnshareArray, will the version of A inside the function be unshared, and hence become a different one from the one in the workspace, disallowing for "simulating" pass by reference?

I'm asking because I want speed and clean code :)

ANSWER @Peters solution is valid. I want to add that it does not seem to copy all the data, that is this code:

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

#include <iostream>

extern "C" bool mxUnshareArray(mxArray *array_ptr, bool noDeepCopy);

void mexFunction(int nargout, mxArray *argout[], int nargin, const mxArray *argin[]) {
    mxUnshareArray(const_cast<mxArray *>(argin[0]), true); 
    double *data = (double *)mxGetData(argin[0]);
    data[0] = data[0]+1;
}

results in this speed:

>> tic; unsharedMexSpeedTest(A); toc; A(1)
Elapsed time is 0.000119 seconds.
ans =
    4

Which is still ~10 times slower than the bare solution. I'm assuming that this is due to the function call overhead, and that the relative speed loss is less worse when the actual operation takes more CPU-cycles.

Was it helpful?

Solution

mxUnshareArray() is undocumented, which means the prototype is not declared in the MATLAB header files. But the symbol should still be available. You just need to write the prototype of the function yourself so that the compiler knows how to form the call. Put this at the top of the file (in global scope) where you need to call it:

extern "C" bool mxUnshareArray(mxArray *array_ptr, bool noDeepCopy);

The "unshare" will keep this from doing very bizarre things:

B = A;
mexSpeedTest(A);
A(1)
B(1)

The variable seen as the input to mexSpeedTest is exactly A; there's no extra copy made for the MEX function call, so mxUnshareArray will do what you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top