虽然没有记录,但 MEX API 函数 mxCreateSharedDataCopy 是是 given as a solution by MathWorks,现在显然已被拒绝,用于创建 mxArray 的共享数据副本。 MathWorks 甚至在他们的解决方案中提供了一个示例,mxsharedcopy.c。
如删除的 MathWorks 解决方案 (1-6NU359) 中所述,该函数可用于克隆 mxArray 标头。但是,plhs[0] = prhs[0]; 和 plhs[0] = mxCreateSharedDataCopy(prhs[0]); 之间的区别在于,第一个版本只是复制 mxArray*(一个指针),因此不会创建新的 mxArray 容器(至少在 mexFunction 返回和MATLAB 的工作原理很神奇),这将增加 mxArrays 中数据的引用计数。
为什么这会是个问题?如果您使用plhs[0] = prhs[0]; 并且在从mexFunction 返回之前未对plhs[0] 进行进一步修改,那么一切都很好,并且由于MATLAB,您将拥有一个共享的数据副本。但是,如果在上述分配之后您修改了 plhs[0] 在 MEX 函数中,那么在 prhs[0] 中也会看到更改,因为它引用了相同的数据缓冲区。另一方面,当显式生成共享副本(使用mxCreateSharedDataCopy)时,有两个不同的mxArray 对象,对一个数组数据的更改将触发复制操作,从而产生两个完全独立的数组。还有,直接赋值can cause segmentation faults in some cases。
修改后的 MathWorks 示例
从使用上述 MathWorks 解决方案中修改后的 mxsharedcopy.c 的示例开始。第一个重要步骤是为mxCreateSharedDataCopy 函数提供原型:
/* Add this declaration because it does not exist in the "mex.h" header */
extern mxArray *mxCreateSharedDataCopy(const mxArray *pr);
正如评论所说,这不在mex.h,所以你必须自己声明。
mxsharedcopy.c 的下一部分通过以下方式创建新的mxArrays:
-
通过mxDuplicateArray进行深拷贝:
copy1 = mxDuplicateArray(prhs[0]);
-
通过mxCreateSharedDataCopy共享副本:
copy2 = mxCreateSharedDataCopy(copy1);
-
直接复制mxArray*,由我添加:
copy0 = prhs[0]; // OK, but don't modify copy0 inside mexFunction!
然后它打印每个 mxArray 的数据缓冲区地址 (pr) 及其第一个值。这是修改后的mxsharedcopy(x) 为x=ones(1e3); 的输出:
prhs[0] = 72145590, mxGetPr = 18F90060, value = 1.000000
copy0 = 72145590, mxGetPr = 18F90060, value = 1.000000
copy1 = 721BF120, mxGetPr = 19740060, value = 1.000000
copy2 = 721BD4B0, mxGetPr = 19740060, value = 1.000000
发生了什么:
- 正如预期的那样,比较
prhs[0] 和copy0,除了指向同一个mxArray 的另一个指针之外,我们没有创建任何新内容。
- 比较
prhs[0] 和copy1,注意mxDuplicateArray 在地址721BF120 处创建了一个新的mxArray,并将数据复制到19740060 处的新缓冲区中。
-
copy2 与 copy1 有不同的地址 (mxArray*),这意味着它也是不同的 mxArray 不仅是由不同变量指向的同一个地址,而是 它们都是在地址19740060共享相同的数据。
问题归结为:在plhs[0] 或copy0 或copy2 中返回是否安全(分别来自简单的指针复制或mxCreateSharedDataCopy)或者是否有必要使用mxDuplicateArray,实际上复制数据?我们可以通过销毁copy1 并验证copy2 仍然有效来证明mxCreateSharedDataCopy 可以工作:
mxDestroyArray(copy1);
copy2val0 = *mxGetPr(copy2); % no crash!
将共享数据副本应用于输入
回到问题。比 MathWorks 示例更进一步,返回输入的共享数据副本。做吧:
if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]);
屏住呼吸!
>> format debug
>> x=ones(1,2)
x =
Structure address = 9aff820 % mxArray*
m = 1
n = 2
pr = 2bcc8500 % double*
pi = 0
1 1
>> xDup = mxsharedcopy(x)
xDup =
Structure address = 9afe2b0 % mxArray* (different)
m = 1
n = 2
pr = 2bcc8500 % double* (same)
pi = 0
1 1
>> clear x
>> xDup % hold your breath!
xDup =
Structure address = 9afe2b0
m = 1
n = 2
pr = 2bcc8500 % double* (still same!)
pi = 0
1 1
现在是临时输入(没有format debug):
>> tempDup = mxsharedcopy(2*ones(1e3));
>> tempDup(1)
ans =
2
有趣的是,如果我在没有 mxCreateSharedDataCopy 的情况下进行测试(即仅使用 plhs[0] = prhs[0];),MATLAB 不会崩溃,但输出变量永远不会实现:
>> tempDup = mxsharedcopy(2*ones(1e3)) % no semi-colon
>> whos tempDup
>> tempDup(1)
Undefined function 'tempDup' for input arguments of type 'double'.
R2013b,Windows,64 位。
mxsharedcopy.cpp(修改后的 C++ 版本):
#include "mex.h"
/* Add this declaration because it does not exist in the "mex.h" header */
extern "C" mxArray *mxCreateSharedDataCopy(const mxArray *pr);
bool mxUnshareArray(const mxArray *pr, const bool noDeepCopy); // true if not successful
void mexFunction(int nlhs,mxArray *plhs[],int nrhs,const mxArray *prhs[])
{
mxArray *copy1(NULL), *copy2(NULL), *copy0(NULL);
//(void) plhs; /* Unused parameter */
/* Check for proper number of input and output arguments */
if (nrhs != 1)
mexErrMsgTxt("One input argument required.");
if (nlhs > 1)
mexErrMsgTxt("Too many output arguments.");
copy0 = const_cast<mxArray*>(prhs[0]); // ADDED
/* First make a regular deep copy of the input array */
copy1 = mxDuplicateArray(prhs[0]);
/* Then make a shared copy of the new array */
copy2 = mxCreateSharedDataCopy(copy1);
/* Print some information about the arrays */
// mexPrintf("Created shared data copy, and regular deep copy\n");
mexPrintf("prhs[0] = %X, mxGetPr = %X, value = %lf\n",prhs[0],mxGetPr(prhs[0]),*mxGetPr(prhs[0]));
mexPrintf("copy0 = %X, mxGetPr = %X, value = %lf\n",copy0,mxGetPr(copy0),*mxGetPr(copy0));
mexPrintf("copy1 = %X, mxGetPr = %X, value = %lf\n",copy1,mxGetPr(copy1),*mxGetPr(copy1));
mexPrintf("copy2 = %X, mxGetPr = %X, value = %lf\n",copy2,mxGetPr(copy2),*mxGetPr(copy2));
/* TEST: Destroy the first copy */
//mxDestroyArray(copy1);
//copy1 = NULL;
//mexPrintf("\nFreed copy1\n");
/* RESULT: copy2 will still be valid */
//mexPrintf("copy2 = %X, mxGetPr = %X, value = %lf\n",copy2,mxGetPr(copy2),*mxGetPr(copy2));
if (nlhs>0) plhs[0] = mxCreateSharedDataCopy(prhs[0]);
//if (nlhs>0) plhs[0] = const_cast<mxArray*>(prhs[0]);
}