Mex inplace change problems R2015b and later

4 views (last 30 days)
This post is to alert mex programmers of a change in R2015b and later that makes inplace changes more hazardous than they used to be (as if this wasn't already tricky enough). The MATLAB changes are of course internal and unpublished, so what follows is just one brief example of a problem you might run into. Changing an input variable inplace of course violates the const rules for the input prhs[ ] array, but is often done by careful mex programmers to avoid the deep data copies that would otherwise be required, particularly when very large variables are involved.
The change: MATLAB R2015b and later now saves reference copies of workspace variables for simple assignments, rather than generating a shared data copy as it used to. The implications of this will be shown below.
The mex routine that changes the first element of the first input argument inplace:
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
if( nrhs && mxIsDouble(prhs[0]) && mxGetNumberOfElements(prhs[0]) > 0 && !mxIsSparse(prhs[0]) ) {
*mxGetPr(prhs[0]) = 99.0;
}
}
R2015a:
>> x = 1:5
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
1 2 3 4 5
R2015b:
>> x = 1:5
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
99 2 3 4 5
WHAT THE ...??? WHAT HAPPENED???
As it turns out, in R2015a and earlier MATLAB apparently generates the 1:5 array on the fly as a new variable and assigns it as expected. The inplace x(1) change works as expected, and because we didn't do anything with x that would cause any data sharing to take place, there are no nasty side effects. When the 1:5 assignment happens again, everything works again as expected.
But in R2015b and later, the assignment of 1:5 causes MATLAB to save a reference copy of the 1:5 variable in the background. Unknown to you, x is in fact sharing with another variable (a background variable that you can't see directly, but you can see it if you hack into it with a mex routine and examine the reference count field). So when you do the inplace change of the x(1), you also inadvertently changed the 1st element of that background reference copy as well. Now for the really nasty part. When you subsequently do another 1:5 assignment, MATLAB apparently remembers that pattern and remembers that it already has a copy of that stored in memory in the background, so it simply uses that for the assignment. Well, now you are screwed because that background reference copy of 1:5 was inadvertently changed inplace so that the 1st element is 99 instead of 1. This really sucks, I know! But that is what you will have to deal with in R2015b and later.
The fix? Well, I know you inplace mex programmers are careful to begin with. But now you are going to have to be really careful how you create & use those variables that are going to be changed inplace. You might have to avoid using any expression (e.g., involving literal values etc) that might allow the parser to outsmart you and save a reference copy. The only advice I can give you is inside your mex routine, use an mxArray header hack to examine the RefCount field to see if you are in trouble before you make the inplace change. Maybe throw an error if the RefCount is anything other than 0.
As stated before, I don't know the rules for this, and actually just discovered this effect recently. Whether this only affects literal assignments as above or other types of expressions I don't know. I haven't really investigated this much yet, but thought I would put this info out there right away.
The reference copy thing is not just for literal assignments btw. For example:
R2015a:
x = 1:5;
y = x; % <-- y will be a shared data copy of x
R2015b:
x = 1:5;
y = x; % <-- y will be a reference copy of x
  5 Comments
James Tursa
James Tursa on 8 Dec 2020
Edited: James Tursa on 8 Dec 2020
This:
% file inplace_change_test.m
x = 0 + 1:5
inplace_change(x)
x
x = 0 + 1:5
y = 0 + 1:5
produces this output:
>> mex inplace_change.c -R2018a
Building with 'Microsoft Visual C++ 2015 (C)'.
MEX completed successfully.
>> version
ans =
'9.8.0.1323502 (R2020a)'
>> inplace_change_test
x =
1 2 3 4 5
x =
99 2 3 4 5
x =
99 2 3 4 5
y =
1 2 3 4 5
So the parser did not use the same caching for y as it did for x in this case, but since parser rules are subject to change without notice I wouldn't necessarily count on this.
Walter Roberson
Walter Roberson on 8 Dec 2020
I edited my post between, as perhaps it matters that a different variable was being assigned to.

Sign in to comment.

Answers (3)

RAB
RAB on 3 Sep 2018
To add to the confusion, in R2015b try running:
>> x = 1:5
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
99 2 3 4 5
>> x = 1: 5 % Note the extra space to change the command
x =
1 2 3 4 5
The caching mechanism in the MATLAB execution engine apparently considers exact syntax and not the "meaning" of the command.
  1 Comment
Walter Roberson
Walter Roberson on 8 Dec 2020
If you have multiple commands on one line, then is it exact match for the entire line that is important, or is it exact match to the point that the statement ends?
e.g. if you had put a semicolon after the assignment would parsing for match stop there?

Sign in to comment.


RAB
RAB on 4 Sep 2018
The command itself does not even need to be changed, just add a comment or just the "%" ...
>> x = 1:5
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
99 2 3 4 5
>> x = 1:5%
x =
1 2 3 4 5
  2 Comments
James Tursa
James Tursa on 4 Sep 2018
But again, using the same syntax again has the same problem. E.g.,
>> x = 1:5
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
99 2 3 4 5 <-- WRONG!!!
>> x = 1:5%
x =
1 2 3 4 5
>> inplace_change(x)
>> x
x =
99 2 3 4 5
>> x = 1:5%
x =
99 2 3 4 5 <-- WRONG AGAIN!!!
RAB
RAB on 5 Sep 2018
Indeed, my example served to illustrate that the caching works on the exact syntax of the command.

Sign in to comment.


James Tursa
James Tursa on 4 Sep 2018
Edited: James Tursa on 4 Sep 2018
One potential workaround is to change an element of the variable after the assignment to force a deep copy. E.g.,
>> x = 1:5
x =
1 2 3 4 5
>> x(1) = x(1); % <-- Force a deep copy, x is no longer a reference copy of the background variable
>> inplace_change(x) % <-- Doesn't alter that background copy of 1:5
>> x
x =
99 2 3 4 5
>> x = 1:5
x =
1 2 3 4 5 <-- Now we get the correct result
Since the background reference copy behavior apparently only occurs for "small" variables this deep copy will not be too costly. E.g., for double class variables background reference copies will be created for variables of size 256 or smaller, but will not be created for variables of size 257 or greater. E.g.,
>> x = 1:256;
>> inplace_change(x)
>> x = 1:256;
>> x(1)
ans =
99 <-- WRONG!!!
>> clear all
>> x = 1:257;
>> inplace_change(x)
>> x = 1:257;
>> x(1)
ans =
1 <-- CORRECT!
  1 Comment
RAB
RAB on 5 Sep 2018
Ok, interesting and makes sense that background copies are not kept for large variables.
I did not look into the caching/background copy behavior for other data types etc. Obviously the goal is to come up with consistent behavior no matter what the size of the variable is.

Sign in to comment.

Categories

Find more on MATLAB Compiler in Help Center and File Exchange

Tags

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!