MEX 调用后 MATLAB 数组分配失败
MATLAB array assignment fails after MEX call
我在使用 MEX 时遇到了奇怪的行为,我将其隔离到以下非常简单的程序中:
#include "mex.h"
#include <stdio.h>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *A;
int i;
if ( nrhs != 1
|| nlhs > 1
|| !mxIsDouble(prhs[0])
|| mxIsComplex(prhs[0])
|| mxGetM(prhs[0])!=1
) mexErrMsgTxt("internal error: dtimes2: input error");
A = mxGetPr(prhs[0]);
for (i=0; i<3; i++) A[i] *= 2;
return; }
所以问题是这样的:在 MATLAB 会话中,
B=[3.2,5.6,9.4]; dtimes2(B); B
MATLAB 表示:B = 6.4000 11.2000 18.8000
到目前为止一切顺利。但是现在:
B=[3.2,5.6,9.4]
MATLAB 表示:B = 6.4000 11.2000 18.8000
但是当我说
B=[-34.5,-57.6,-28.9]
然后 MATLAB 说:B = -34.5000 -57.6000 -28.9000
你看中间如果数字和之前一样我不能重新分配B。所以,现实检查:
A=[1,2,3]; A=2*A; A=[1,2,3]
当然有效:MATLAB 最后说 A = [1,2,3]。
MEX 警告我的编译器是“6.2.1-2”,但支持的编译器是“4.7.x”,但对于这个简单的程序,我几乎没有想到会出现问题。这里出了什么问题?
您不应该修改进入 MEX 包装器的任何输入。这是 未定义的行为,这就是您的 MEX 函数中发生的情况。 MEX 建议您 return 输出而不是改变输入。如果您真的想要改变输入或他所说的就地编辑,您可以在 Yair Altman 的未记录的 MATLAB 博客中阅读更多相关信息。不过,当您开始使用 MEX 时,我不鼓励这种行为。尽管在某些情况下它是值得的,但除非您知道自己在做什么,否则请尽可能避免这种情况:http://undocumentedmatlab.com/blog/matlab-mex-in-place-editing。
至于不修改输入,这在你的mexFunction
网关的函数声明中有明确定义:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
^^^^^^^^^^^^^^^^^^^^^
const
修饰符确保输入指针的 none 被更改,这很好...但这并不能阻止您实际修改指向内存的指针的内容就地 引用或执行此操作。一般来说,由于 "lazy copy" 行为,您在 MATLAB 工作区中为任何 MATLAB 变量创建的内存可能会与其他 MATLAB 变量共享。因此,您在矩阵、向量或单个值中看到的值可能会链接到其他矩阵、向量或其他变量的单个值。当您就地更改内存时,您也会更改其他变量,这可能是您看到不一致行为的原因。
始终return实际需要的输出而不是改变输入,因为您将保证获得正确的结果。我在下面修改了您的代码,以便它输出一个新矩阵,而不是就地执行计算。
#include "mex.h"
#include <stdio.h>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *A;
double *B; // Change - For the output
int i;
if ( nrhs != 1
|| nlhs > 1
|| !mxIsDouble(prhs[0])
|| mxIsComplex(prhs[0])
|| mxGetM(prhs[0])!=1
) mexErrMsgTxt("internal error: dtimes2: input error");
A = mxGetPr(prhs[0]);
// Change - Create output memory
mwSize rows = mxGetM(prhs[0]);
mwSize cols = mxGetN(prhs[0]);
plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);
// Change - get a pointer to the output memory
B = mxGetPr(plhs[0]);
for (i=0; i<3; i++) B[i] = 2*A[i]; // Change - Write to output memory
return;
}
然后你会这样做:
B = dtimes2(B);
我只是在想发生了什么以及它是如何发生的,并想做一个笔记来强调从 MATLAB 下更改不可变 MATLAB 对象的危险,以防帮助任何人 e
给定 A=[1,2,3],MATLAB 必须计算 [1,2,3] 的散列,比如 "boo",并将 [1,2,3] 存储在 bin "boo" 中。在MEX文件中,它愉快地放弃了指向[1,2,3]的指针,而强大的c代码将那个地址的向量变成了[2,4,6]。然后,在语句A=[1,2,3]上,MATLAB再次计算hash,还是一样,即"boo"。由于会话很简单,因此该垃圾箱中只有一个对象。好吧,如果只有一个对象,它必须已经是 [1,2,3],那么为什么要将它重新写入内存呢?所以它没有。但是内存不是 [1,2,3],而是 [2,4,6],它不应该在 bin "boo" 中。违规后的结果是下面的对话:
我:MATLAB,请设置A=[1,2,3].
MATLAB:先生,是的,先生!做过某事! A=[2,4,6]!
我:不,真的,设置 A=[1,2,3].
MATLAB:先生,是的,先生! A=[2,4,6]!
我:好的。嗯,设置 A=[3,4,5].
MATLAB:先生,是的,先生! A=[3,4,5]。
我:那么,嗯,好的,现在设置 A=[1,2,3]
MATLAB:先生,是的,先生! A=[2,4,6]!
每次执行错误的 MEX 文件时,生成的向量都位于错误的哈希箱中。随机地,一些垃圾箱恰好有一个错误的对象。这引起了极大的混乱。
我在使用 MEX 时遇到了奇怪的行为,我将其隔离到以下非常简单的程序中:
#include "mex.h"
#include <stdio.h>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *A;
int i;
if ( nrhs != 1
|| nlhs > 1
|| !mxIsDouble(prhs[0])
|| mxIsComplex(prhs[0])
|| mxGetM(prhs[0])!=1
) mexErrMsgTxt("internal error: dtimes2: input error");
A = mxGetPr(prhs[0]);
for (i=0; i<3; i++) A[i] *= 2;
return; }
所以问题是这样的:在 MATLAB 会话中,
B=[3.2,5.6,9.4]; dtimes2(B); B
MATLAB 表示:B = 6.4000 11.2000 18.8000
到目前为止一切顺利。但是现在:
B=[3.2,5.6,9.4]
MATLAB 表示:B = 6.4000 11.2000 18.8000
但是当我说
B=[-34.5,-57.6,-28.9]
然后 MATLAB 说:B = -34.5000 -57.6000 -28.9000
你看中间如果数字和之前一样我不能重新分配B。所以,现实检查:
A=[1,2,3]; A=2*A; A=[1,2,3]
当然有效:MATLAB 最后说 A = [1,2,3]。
MEX 警告我的编译器是“6.2.1-2”,但支持的编译器是“4.7.x”,但对于这个简单的程序,我几乎没有想到会出现问题。这里出了什么问题?
您不应该修改进入 MEX 包装器的任何输入。这是 未定义的行为,这就是您的 MEX 函数中发生的情况。 MEX 建议您 return 输出而不是改变输入。如果您真的想要改变输入或他所说的就地编辑,您可以在 Yair Altman 的未记录的 MATLAB 博客中阅读更多相关信息。不过,当您开始使用 MEX 时,我不鼓励这种行为。尽管在某些情况下它是值得的,但除非您知道自己在做什么,否则请尽可能避免这种情况:http://undocumentedmatlab.com/blog/matlab-mex-in-place-editing。
至于不修改输入,这在你的mexFunction
网关的函数声明中有明确定义:
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
^^^^^^^^^^^^^^^^^^^^^
const
修饰符确保输入指针的 none 被更改,这很好...但这并不能阻止您实际修改指向内存的指针的内容就地 引用或执行此操作。一般来说,由于 "lazy copy" 行为,您在 MATLAB 工作区中为任何 MATLAB 变量创建的内存可能会与其他 MATLAB 变量共享。因此,您在矩阵、向量或单个值中看到的值可能会链接到其他矩阵、向量或其他变量的单个值。当您就地更改内存时,您也会更改其他变量,这可能是您看到不一致行为的原因。
始终return实际需要的输出而不是改变输入,因为您将保证获得正确的结果。我在下面修改了您的代码,以便它输出一个新矩阵,而不是就地执行计算。
#include "mex.h"
#include <stdio.h>
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
{
double *A;
double *B; // Change - For the output
int i;
if ( nrhs != 1
|| nlhs > 1
|| !mxIsDouble(prhs[0])
|| mxIsComplex(prhs[0])
|| mxGetM(prhs[0])!=1
) mexErrMsgTxt("internal error: dtimes2: input error");
A = mxGetPr(prhs[0]);
// Change - Create output memory
mwSize rows = mxGetM(prhs[0]);
mwSize cols = mxGetN(prhs[0]);
plhs[0] = mxCreateDoubleMatrix(rows, cols, mxREAL);
// Change - get a pointer to the output memory
B = mxGetPr(plhs[0]);
for (i=0; i<3; i++) B[i] = 2*A[i]; // Change - Write to output memory
return;
}
然后你会这样做:
B = dtimes2(B);
我只是在想发生了什么以及它是如何发生的,并想做一个笔记来强调从 MATLAB 下更改不可变 MATLAB 对象的危险,以防帮助任何人 e 给定 A=[1,2,3],MATLAB 必须计算 [1,2,3] 的散列,比如 "boo",并将 [1,2,3] 存储在 bin "boo" 中。在MEX文件中,它愉快地放弃了指向[1,2,3]的指针,而强大的c代码将那个地址的向量变成了[2,4,6]。然后,在语句A=[1,2,3]上,MATLAB再次计算hash,还是一样,即"boo"。由于会话很简单,因此该垃圾箱中只有一个对象。好吧,如果只有一个对象,它必须已经是 [1,2,3],那么为什么要将它重新写入内存呢?所以它没有。但是内存不是 [1,2,3],而是 [2,4,6],它不应该在 bin "boo" 中。违规后的结果是下面的对话:
我:MATLAB,请设置A=[1,2,3].
MATLAB:先生,是的,先生!做过某事! A=[2,4,6]!
我:不,真的,设置 A=[1,2,3].
MATLAB:先生,是的,先生! A=[2,4,6]!
我:好的。嗯,设置 A=[3,4,5].
MATLAB:先生,是的,先生! A=[3,4,5]。
我:那么,嗯,好的,现在设置 A=[1,2,3]
MATLAB:先生,是的,先生! A=[2,4,6]!
每次执行错误的 MEX 文件时,生成的向量都位于错误的哈希箱中。随机地,一些垃圾箱恰好有一个错误的对象。这引起了极大的混乱。