Mex inplace change problems R2015b and later

8 次查看(过去 30 天)
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 个评论
James Tursa
James Tursa 2020-12-8
编辑:James Tursa 2020-12-8
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 2020-12-8
I edited my post between, as perhaps it matters that a different variable was being assigned to.

请先登录,再进行评论。

回答(3 个)

RAB
RAB 2018-9-3
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 个评论
Walter Roberson
Walter Roberson 2020-12-8
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?

请先登录,再进行评论。


RAB
RAB 2018-9-4
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 个评论
James Tursa
James Tursa 2018-9-4
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 2018-9-5
Indeed, my example served to illustrate that the caching works on the exact syntax of the command.

请先登录,再进行评论。


James Tursa
James Tursa 2018-9-4
编辑:James Tursa 2018-9-4
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 个评论
RAB
RAB 2018-9-5
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.

请先登录,再进行评论。

类别

Help CenterFile Exchange 中查找有关 Logical 的更多信息

标签

Community Treasure Hunt

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

Start Hunting!

Translated by