When a MEX file is called using mexMakeMemoryPersistent, how are variables re-assigned their pointers on subsequent calls?
我正在尝试从MATLAB的C MEX API绕过
我不明白-当多次使用MEX文件并使用
例如,假设我有一个名为myFunc
的MEX文件
它包含以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | int* myVar = NULL; void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if(myVar == NULL) { myVar = (int*)mxCalloc(sizeof(int*), 10); mexMakeMemoryPersistent(myVar); myVar[0] = 1; } // Do the thing I want with it myVar[0] *= 2; } |
我们第一次做
在MATLAB中,很明显,
但是在返回之后又被再次调用该怎么办:
我们告诉MATLAB不要破坏我们刚刚为
它如何解决需要将什么变量传递给该指针,并实际进行传递?
我没有麻烦使用它,我只是想从概念上理解它,因为我认为它确实很整洁。
MEX文件在首次调用时就会加载到内存中。此时,MEX文件中的全局变量将在内存中获取一个位置并进行初始化。接下来调用
下次调用MEX文件时,该文件仍会加载到内存中,并且这些全局变量仍然存在。这次MATLAB只需要调用
在MATLAB中执行
下次调用MEX文件时,就像第一次调用该文件。
请注意,由于MEX文件是已编译的二进制文件,因此变量名不再可见(调试信息中除外)。机器码只处理内存地址和寄存器。
在C的情况下,作用域规则,继承等都只是导致可以使用C或任何其他编译语言获得的同一机器代码的抽象。
一些情况需要澄清:
1 2 3 4 5 6 7 8 9 10 11 12 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { static int* myVar = NULL; // This is basically the same as a global myVar in this case if(myVar == NULL) { myVar = (int*)mxCalloc(sizeof(int*), 10); //mexMakeMemoryPersistent(myVar); // Let's leave out this line! myVar[0] = 1; } myVar[0] *= 2; } |
在上述情况下,我们没有使内存持久化。
1 2 3 4 5 6 7 8 9 10 11 12 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { int* myVar = NULL; // This is now a local variable if(myVar == NULL) { myVar = (int*)mxCalloc(sizeof(int*), 10); mexMakeMemoryPersistent(myVar); myVar[0] = 1; } myVar[0] *= 2; } |
在上述情况下,每次调用MEX文件时,
1 2 3 4 5 6 7 8 9 10 11 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { static int* myVar = NULL; if(myVar == NULL) { myVar = (int*)malloc(sizeof(int*), 10); // Using the system malloc myVar[0] = 1; } myVar[0] *= 2; } |
在上述情况下,一切正常,但从未释放过由
注册一个当MEX文件存在时要运行的函数。
1 2 3 4 5 | void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { static int myVar[10] = {1}; myVar[0] *= 2; } |
在上述情况下,我们使用静态变量保存所有数据,不使用动态内存分配,我们无需担心内存泄漏。除非数组很大,否则我建议这样做。
对于我想清除的mexMakeMemoryPersistent和mexMakeArrayPersistent的工作方式似乎有些困惑。免责声明:以下内容基于我从已经执行的测试中观察到的行为...不一定在官方的MATLAB文档中。
mxArray变量标头有一个字段,我将称之为VariableType,该字段包含一个指示其类型的变量的值。例如,对于R2018b和更早版本:
1 2 3 4 5 6 | struct mxArray_header { void *RevCrossLink; mxClassID ClassID; int VariableType; : etc. |
对于工作区中的变量,VariableType将为0(正常)。通常,传递给prhs []的变量将是普通类型。
所有分配内存(mxArray变量或原始内存)的官方API函数的所有(*)将该内存的地址放在mex例程的MATLAB Memory Manager临时分配列表中。注意:mxArray变量(即,mxGetPr()和朋友后面的东西)的"数据"存储器不在此分配列表中……其配置完全取决于它所属于的mxArray。使用官方API函数创建的mxArrays的VariableType是4(临时)。
(*)mxArrayToString()曾经是一个例外,但在R2017a中已得到修复。
当mex例程退出时,据我所知,发生了以下事情:
plhs []变量的共享数据副本已创建(这些
是实际传递回调用方的内容。
此mex例程的临时分配列表上的所有内容都是
销毁/释放。
在这种背景下,持久性功能将执行以下操作:
mexMakeMemoryPersistent(内存地址)
- 从临时内存分配列表中删除memory_address
mexMakeArrayPersistent(mxArray_address)
- 从临时mxArray分配列表中删除mxArray_address
- 将mxArray_address之后的mxArray的VariableType更改为0(正常)
而且,实际上,mexMakeMemoryPersistent的文档规定以下内容:
"如果您创建持久性内存,则在清除MEX函数时负责释放它。如果不释放内存,则MATLAB会泄漏内存。"
最重要的是,您必须手动销毁/释放持久内存...一旦使内存持久,MATLAB内存管理器将不再为您提供帮助。当从内存中清除mex函数时,尤其如此。无论您是否有全局变量存储持久性存储器,也不管它最初来自于官方的MATLAB API函数,您的持久性内存都将泄漏。您需要使用mexAtExit和/或mexLock / mexUnlock函数的某种组合来管理这种情况,以免发生内存泄漏。据我所知,这一直是这些持久功能的行为。
侧面注意:没有官方的API函数可以执行相反的操作...,即,您不能再次使持久性存储器成为非持久性。一旦使某些东西持久化,就必须坚持下去,必须手动对其进行处理。
具有100MB内存块的演示:
1 2 3 4 5 6 7 8 9 10 11 | /* persist_test.c */ #include"mex.h" char *cp = NULL; #define ONE_MB (1024*1024) void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if( cp == NULL ) { cp = mxMalloc(100*ONE_MB); mexMakeMemoryPersistent(cp); } } |
在命令行上,即使从内存中清除了mex例程(内存使用量不断增加而从未减少),也清楚地显示了内存泄漏:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | >> memory Maximum possible array: 2324 MB (2.436e+09 bytes) * Memory available for all arrays: 2324 MB (2.436e+09 bytes) * Memory used by MATLAB: 1012 MB (1.061e+09 bytes) Physical Memory (RAM): 8056 MB (8.447e+09 bytes) * Limited by System Memory (physical + swap file) available. >> persist_test >> memory Maximum possible array: 2183 MB (2.289e+09 bytes) * Memory available for all arrays: 2183 MB (2.289e+09 bytes) * Memory used by MATLAB: 1115 MB (1.169e+09 bytes) Physical Memory (RAM): 8056 MB (8.447e+09 bytes) * Limited by System Memory (physical + swap file) available. >> [~,mexnames] = inmem mexnames = 'winqueryreg' 'persist_test' >> clear persist_test >> [~,mexnames] = inmem mexnames = 'winqueryreg' >> memory Maximum possible array: 2174 MB (2.279e+09 bytes) * Memory available for all arrays: 2174 MB (2.279e+09 bytes) * Memory used by MATLAB: 1103 MB (1.157e+09 bytes) Physical Memory (RAM): 8056 MB (8.447e+09 bytes) * Limited by System Memory (physical + swap file) available. >> >> % Do it again >> >> persist_test >> memory Maximum possible array: 2053 MB (2.153e+09 bytes) * Memory available for all arrays: 2053 MB (2.153e+09 bytes) * Memory used by MATLAB: 1206 MB (1.265e+09 bytes) Physical Memory (RAM): 8056 MB (8.447e+09 bytes) * Limited by System Memory (physical + swap file) available. >> [~,mexnames] = inmem mexnames = 'winqueryreg' 'persist_test' >> clear persist_test >> [~,mexnames] = inmem mexnames = 'winqueryreg' >> memory Maximum possible array: 2073 MB (2.174e+09 bytes) * Memory available for all arrays: 2073 MB (2.174e+09 bytes) * Memory used by MATLAB: 1202 MB (1.260e+09 bytes) Physical Memory (RAM): 8056 MB (8.447e+09 bytes) * Limited by System Memory (physical + swap file) available. >> |