使用 mexMakeMemoryPersistent 调用 MEX 文件时,变量如何在后续调用中重新分配其指针?

When a MEX file is called using mexMakeMemoryPersistent, how are variables re-assigned their pointers on subsequent calls?

我正在尝试从 MATLAB 的 C MEX API.

中绕过 mexMakeMemoryPersistent()

我不明白 - 当一个 MEX 文件被多次调用时,mexMakeMemoryPersistent() 已被使用,它分配的内存在第二次调用时如何返回给 MEX 文件?

例如,假设我有一个名为 myFunc 的 MEX 文件

它包含以下内容:

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;
}

我们第一次做

fx>> myFunc()

在 MATLAB 中,很明显 myVar 将是 NULL,然后 mxCalloc 将获取一些内存并获取指向它的指针,等等

但是在它 returns 之后呢,然后被调用 时间:

fx>> myFunc()

我们告诉 MATLAB 不要破坏我们刚刚为 myVar 分配的内存。但是,当 myFunc 的新实例是 运行 时,该指针如何重新分配给 myVar?如果不是,myVar 将只是 NULL,我们会回到第一个方块。它是否维护变量名列表和分配给它们的内存?但是如果我们有奇怪的作用域规则、继承(在 C++ 的情况下)等会发生什么?它如何解析 什么 变量需要传递给那个指针,并实际进行传递?

我使用这个没有问题,我只是想从概念上理解它,因为我认为它真的很简洁。

MEX-files 在第一次调用时加载到内存中。此时,MEX-file中的全局变量在内存中得到一个位置并被初始化。接下来调用 mexFunction,您的代码将有机会分配内存并将指针分配给此全局变量。

下次调用MEX-file时,它仍然加载在内存中,那些全局变量仍然存在。这次MATLAB只需要调用mexFunction.

当您在 MATLAB 中执行 clear mexclear all 时,MEX-file 将从内存中卸载。此时全局变量将停止存在。 因为您使用mxCalloc分配内存,MATLAB 可以回收丢失的内存。如果您使用 calloc 代替,此时您会泄漏内存。 您不再拥有指向已分配内存的指针,因此此时您已泄漏内存(请参阅 下面)。

下次调用 MEX-file 时,将与第一次调用时相同。


请注意,由于 MEX-file 是已编译的二进制文件,您的变量名称不再可见(调试信息除外)。机器码只处理内存地址和寄存器。

在 C++ 的情况下,作用域规则、继承等都只是抽象,它们导致您可以使用 C 或任何其他编译语言获得相同的机器代码。


几个案例澄清事情:

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;
}

在上面的例子中,我们没有让内存持久化。 myVar 指针在 MEX-file 调用中保留,但不是指向的内存。第二次调用 MEX-file,myVar[0] *= 2 会做一些非法的事情,可能会使 MATLAB 崩溃。

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-file 时,myVar 都将为 NULL,因此每次都会分配新的内存。内存是持久的,所以最终你会 运行 内存不足。

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;
}

在上面的例子中,一切正常,除了 malloc 分配的内存永远不会被释放。当你做clear allclear mex时,MEX-file会被清除,myVar静态变量会被删除,但是malloc分配的内存还在.你又在泄漏内存。如果你想这样做,你需要在 MEX-file 存在时使用 mexAtExit().

注册一个函数为 运行
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{
    static int myVar[10] = {1};
    myVar[0] *= 2;
}

在上面的例子中,我们使用静态变量来保存我们所有的数据,没有使用动态内存分配,我们不需要担心内存泄漏。我推荐这个,除非数组非常大。

关于 mexMakeMemoryPersistent 和 mexMakeArrayPersistent 的工作方式似乎有些混乱,我想澄清一下。免责声明:以下内容基于我从我执行的测试中观察到的行为......不一定在官方 MATLAB 文档中。

mxArray 变量 headers 有一个我称之为 VariableType 的字段,它包含一个指示变量类型的值。例如,对于 R2018b 及更早版本:

struct mxArray_header {
    void *RevCrossLink;
    mxClassID ClassID;
    int VariableType;
    :
    etc.

对于工作区中的变量,VariableType 将为 0(正常)。通常,传递给 prhs[ ] 的变量将是普通类型。

分配内存(mxArray 变量或原始内存)的官方 API 函数的所有 (*) 将该内存的地址放在 mex 例程的 MATLAB 内存管理器临时分配列表中。注意:mxArray 变量的 "data" 内存(即 mxGetPr( ) 和朋友背后的东西)在此分配列表上 ... 它的配置完全取决于在 mxArray 上它是的一部分。用官方API函数创建的mxArrays的VariableType是4(临时的)。

(*) mxArrayToString( ) 曾经是一个例外,但在 R2017a 中已修复。

当 mex 例程退出时,据我所知会发生以下情况:

  1. 创建了 plhs[ ] 变量的共享数据副本(这些 是实际传递回调用者的内容)。

  2. 这个 mex 例程的临时分配列表上的所有内容都是 destroyed/freed.

在这种背景下,持久函数的作用如下:

mexMakeMemoryPersistent(memory_address)

  • 从临时内存分配列表中删除 memory_address

mexMakeArrayPersistent(mxArray_address)

  • 从临时 mxArray 分配列表中删除 mxArray_address
  • 将mxArray_address后面的mxArray的VariableType改为0(正常)

事实上,mexMakeMemoryPersistent 的文档说明如下:

"If you create persistent memory, you are responsible for freeing it when the MEX function is cleared. If you do not free the memory, MATLAB leaks memory."

底线是,您必须手动destroy/free永久内存...一旦您使内存持久化,MATLAB 内存管理器将不再帮助您。当从内存中清除 mex 函数时尤其如此。你的持久内存 被泄露,不管你有一个全局变量存储它,也不管它最初来自官方 MATLAB API 函数。您需要使用 mexAtExit and/or mexLock/mexUnlock 函数的某种组合来管理这种情况,这样您就不会发生内存泄漏。据我所知,这一直是这些持久函数的行为。

旁注:没有官方的 API 函数可以执行相反的操作...即,您无法再次创建持久内存 non-persistent。一旦你让某件事持久化,你就会被它困住,必须手动处理它。

100MB内存块的演示:

/* 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 例程,也清楚地显示了内存泄漏(内存使用量不断上升,永不下降):

>> 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.
>>