|
I thought I'd add a note that you have to be careful how
you call the example. If you try:
var_struct_copied = foo(var_struct(2), value,
duplicate_array);
it will cause badness. As far as I can work out Matlab
recognises that var_struct(2) is a temporary variable, so
the line:
p_return = mxCreateSharedCopy(p_in);
sets p_return = p_in;
Unsharing p_return and destroying its fields is then bad.
I add a line to check if (p_return == p_in) and if it is
just set the fields of p_return without any of the
unsharing (complete abuse of the const declaration since it
doesn't matter because the input is temporary). If there
are nested structures you still need the unsharing for them
though (if you were altering var_struct(2).a.b you need the
shared copy of var_struct(2).a).
I also had a question about the second part of this
statement:
> y{1}:
> mxDestroyArray(first_cell_copy);
> mxSetCell(y, 0, new_mxArray);
> or by unsharing it and assign the data pointer:
> mxUnshareArray(first_cell_copy);
> mxSetData ...
I have been using:
mxDestroyArray(first_cell_copy);
mxSetCell(y, 0, mxDuplicateArray(first_cell));
Can I use first_cell_copy instead? Something like:
mxUnshareArray(first_cell_copy);
mxSetCell(y, 0, first_cell_copy);
I'm not sure how to use the mxSetData here...
It seems I'm making two copies of the first cell.
Cheers,
Andrew
schubert.ing@googlemail.com wrote in message
<1180956980.030482.78450@n4g2000hsb.googlegroups.com>...
> I'm sorry the last message has been messed up due to
inserted
> linebreaks. This one should look better.
>
> Beware: quite long post
> For the ASCII graphics below being properly displayed, it
is
> recommended to use a fixed-width font.
>
> This article describes how to change a cell of a cell
array or a field
> of a structure array in a mex-function without
duplicating the whole
> array. Since the right-hand-side arguments must not be
changed, one
> typically creates a deep copy of the array using the
function
> "mxDuplicateArray" and changes the field in the copy. A
more cunning
> way is to create a shared copy, making it uncessery to
copy the fields
> or cells not being changed.
>
>
> The internal structure of how Matlab stores a variable
and the concept
> of sharing variables using crosslinks is described in an
article from
> Peter Boetcher:
> http://groups.google.com/group/comp.soft-
sys.matlab/browse_thread/thread/c241d8821fb90275/47189d498d1
f45b8?lnk=st&q=&rnum=1&hl=en#47189d498d1f45b8
>
> For creating shared copies of an mxArray, the functions
> "mxCreateSharedCopy", "mxUnshareArray",
and "mxUnreference" are used.
> Since not documented, the following might not be correct.
But it has
> been tested and seems to work for Matlab versions R14,
2006b, 2007a.
> Any ideas about how doing it better are welcome.
>
> Examine the disassembly, I guess that the three functions
have the
> following syntax:
>
> mxArray *mxCreateSharedCopy(const mxArray *pr);
> bool mxUnshareArray(const mxArray *pr, const bool
noDeepCopy); //
> true if not successful
> mxArray *mxUnreference(const mxArray *pr);
>
> They are described in the following. The const
declaration is not
> fully correct since the array headers change. But with
it, the
> compiler does not complain about different const
declarations when
> assigning right-hand-side arguments.
>
>
> Take a variable x created in Matlab:
> x = {[1 2], [3 4]};
>
> In a C-Mex-File, x looks as follows:
>
> mxArray *x;
> x points to an mxArray whose data pointer points to an
array of two
> pointers, each pointing to an mxArray (an mxArray is
indicated by a
> rectangle):
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ /
\ /
> +==================+
+==================+
> | crosslink = NULL | |
crosslink = NULL |
> | refcount = 0 | |
refcount = 0 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
>
| |
>
\ / \ /
> [1 2]
[3 4]
>
>
> If we want to change the content of the first cell, we
first must
> create a copy of x. The following procedere is equivalent
for
> structure arrays, except the mxGetCell and mxSetCell
functions must be
> replaced.
>
> mxArray *y = mxCreateSharedCopy(x);
> The data pointer of y points to the same array as x does.
A crosslink
> is set between x and y:
> *x:
> +==================+
> | crosslink = y |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ / \ | |
> | \ /
\ /
> | +==================+
+==================+
> | | crosslink = NULL | |
crosslink = NULL |
> | | refcount = 0 | |
refcount = 0 |
> | | pdata -+ | |
pdata -+ |
> | +=========|========+
+=========|========+
> |
| |
> |
\ / \ /
> | [1 2]
[3 4]
> *y: |
> +==================+ |
> | crosslink = x | |
> | refcount = 0 | |
> | pdata ----------+---+
> +==================+
>
>
>
> mxUnshareArray(y, true); // true: do not make a
deep copy
> The array of mxArray-pointers is copied and the crosslink
between x
> and y is removed. The reference counter (refcount) of
each mxArray in
> the cell is increased by one.
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ / \ /
> +==================+
+==================+
> | crosslink = NULL | |
crosslink = NULL |
> | refcount = 1 | |
refcount = 1 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
> / \ | /
\ |
> | \ /
| \ /
> | [1 2] |
[3 4]
> *y: | |
> +==================+ | |
> | crosslink = NULL | | |
> | refcount = 0 | | |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+
>
>
>
> Now we want to change the first cell of y. The shared
mxArray has to
> be copied using the function "mxUnreference".
> Get the first mxArray:
> mxArray *first_cell = mxGetCell(y, 0);
> Note that the following yields the same result, since the
mxArray only
> exists once (y is replaced by x):
> mxArray *first_cell = mxGetCell(x, 0);
>
> The mxUnreference function copies the mxArray and creates
a crosslink
> between the copies. The affected reference counter is
decreased by 1:
> mxArray *first_cell_copy = mxUnreference(first_cell);
> *x:
> +==================+
> | crosslink = NULL |
> | refcount = 0 |
> | pdata ----------+------> [mxArray*
mxArray*]
> +==================+ | |
> \ / \ /
> +==================+
+==================+
> *first_cell:| crosslink = f_c_c| |
crosslink = NULL |
> | refcount = 0 | |
refcount = 1 |
> | pdata -+ | |
pdata -+ |
> +=========|========+
+=========|========+
> | <-----+ /
\ |
> \ / |
| \ /
> [1 2] | |
[3 4]
> *y: | |
> +==================+ | |
> | crosslink = NULL | | |
> | refcount = 0 | | |
> | pdata ----------+------> [mxArray* |
mxArray*]
> +==================+ | |
> \ / |
> +=================|+
> *first_cell_copied:| crosslink = f_c ||
> | refcount = 0 ||
> | pdata ---------+|
> +==================+
>
>
> To assign a new value to y{1}, the crosslink between
first_cell and
> first_cell_copied must be removed. This can be done by
either
> destroying the mxArray first_cell_copy and assigning a
new one to
> y{1}:
> mxDestroyArray(first_cell_copy);
> mxSetCell(y, 0, new_mxArray);
> or by unsharing it and assign the data pointer:
> mxUnshareArray(first_cell_copy);
> mxSetData ...
>
> If first_cell is another cell or structure array, one can
recursively
> repeat all the steps above (after mxCreateShareCopy). It
is noteworthy
> that all this can also be applied on objects, since
objects in Matlab
> are stored as structure arrays.
>
> Below is a program demonstrating the use of the three
functions.
> Suggestions for improvements are welcome.
>
> I hope this is useful to someone. In my case, it gives a
great
> performance benefit when modifying a certain property of
a large
> number of objects.
>
>
>
>
>
> Example program:
> /
>
************************************************************
************
> * syntax in Matlab:
> * function var_struct_copied = foo(var_struct, value,
> duplicate_array)
> *
> * Returns the structure variable "var_struct" with the
first field
> set to
> * "value". If "duplicate_array" is false or omitted, a
shared copy is
> * created. Otherwise a deep copy of the struct array is
returned
> * (using "mxDuplicateArray").
>
>
************************************************************
*************/
>
>
> #include "mex.h"
>
> // declare undocumented functions
> extern mxArray *mxCreateSharedCopy(const mxArray *pr);
> extern bool mxUnshareArray(const mxArray *pr, const bool
noDeepCopy);
> extern mxArray *mxUnreference(const mxArray *pr);
>
>
> /* Definition of structure mxArray_tag for debugging
purposes. Might
> not
> * be fully correct for Matlab 2006b or 2007a, but it
works.
> * Thanks to Peter Boettcher. */
> struct mxArray_tag {
> const char *name;
> mxClassID class_id;
> int vartype;
> mxArray *crosslink;
> int number_of_dims;
> int refcount;
> struct {
> unsigned int scalar_flag : 1;
> unsigned int flag1 : 1;
> unsigned int flag2 : 1;
> unsigned int flag3 : 1;
> unsigned int flag4 : 1;
> unsigned int flag5 : 1;
> unsigned int flag6 : 1;
> unsigned int flag7 : 1;
> unsigned int private_data_flag : 1;
> unsigned int flag8 : 1;
> unsigned int flag9 : 1;
> unsigned int flag10 : 1;
> unsigned int flag11 : 4;
> unsigned int flag12 : 8;
> unsigned int flag13 : 8;
> } flags;
> int rowdim;
> int coldim;
> union {
> struct {
> double *pdata; // original: void*
> double *pimag_data; // original: void*
> void *irptr;
> void *jcptr;
> int nelements;
> int nfields;
> } number_array;
> struct {
> mxArray **pdata;
> char *field_names;
> void *dummy1;
> void *dummy2;
> int dummy3;
> int nfields;
> } struct_array;
> struct {
> void *pdata; /*mxGetInfo*/
> char *field_names;
> char *name;
> int checksum;
> int nelements;
> int reserved;
> } object_array;
> } data;
> };
>
>
>
> // Matlab gatway function
> void mexFunction(int nlhs, mxArray *plhs[],
> int nrhs, const mxArray *prhs[])
> {
> const mxArray *p_in,
> *mx_value; // value to be
assigned
> mxArray *p_return; // mxArray to be
returned
> bool duplicate_array = false; // duplicate or
shallow?
>
>
>
> if (nrhs < 2) {
> mexErrMsgTxt("Two input arguments are expected.");
> } else {
> p_in = prhs[0];
> mx_value = prhs[1];
> if (!mxIsStruct(p_in))
> mexErrMsgTxt("First argument must be a
structure array.");
> }
>
> if (nrhs == 3) {
> const mxArray *mx_arg = prhs[2];
> if (mxIsLogicalScalar(mx_arg)) {
> duplicate_array = *mxGetLogicals(mx_arg);
> } else
> mexErrMsgTxt("Third argument must be a
logical scalar.");
> }
>
>
> if (duplicate_array) {
> // duplicate given arrays, set value and return
> p_return = mxDuplicateArray(p_in);
> mxSetFieldByNumber(p_return, 0, 0,
> mxDuplicateArray(mx_value));
>
> } else {
> mxArray *mx_field, *mx_field_shared;
>
> // create shared copy
> p_return = mxCreateSharedCopy(p_in);
>
> // unshare array
> mxUnshareArray(p_return, true);
>
> // unreference first field
> mx_field = mxGetFieldByNumber(p_return, 0, 0);
> mx_field_shared = mxUnreference(mx_field);
> // mxArray *mx_field has been copied and is shared
> (crosslinked) with *mx_field_shared
>
> // destroy shared array (removes crosslink),
since we want to
> set a new one
> mxDestroyArray(mx_field_shared);
>
> // set new array
> mxSetFieldByNumber(p_return, 0, 0,
> mxCreateSharedCopy(mx_value));
> }
>
> plhs[0] = p_return;
> }
>
|