Когда MEX-файл вызывается с использованием mexMakeMemoryPersistent, как переменным назначаются их указатели при последующих вызовах? - PullRequest
1 голос
/ 13 июня 2019

Я пытаюсь обернуть голову вокруг mexMakeMemoryPersistent() из C MATLAB API C MEX.

Я не понимаю - когда MEX-файл вызывается более одного раза, с использованием mexMakeMemoryPersistent()как память, которая ей назначается, передается обратно в MEX-файл, когда он вызывается второй раз?

Например, скажем, у меня есть MEX-файл с именем myFunc

, и он содержит следующее:

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захватить некоторую память и получить указатель на нее и т. д.

Но что после того, как он вернется, а затем будет назван секунда время:

fx>> myFunc()

Мы сказали MATLAB не уничтожать память, которую мы только что выделили для myVar.Но как этот указатель переназначается в myVar при запуске нового экземпляра myFunc?Если этого не произойдет, myVar будет просто NULL, и мы вернемся на круги своя.Поддерживает ли он список имен переменных и память, которая была им назначена?Но что произойдет, если у нас будут странные правила области видимости, наследование (в случае C ++) и т. Д.?Как он разрешает , какую переменную нужно передать этому указателю, и на самом деле выполнить передачу?

У меня нет проблем с использованием этого, я просто хочу понять это концептуально, потому что я думаю,это действительно опрятно.

Ответы [ 2 ]

2 голосов
/ 13 июня 2019

MEX-файлы загружаются в память при первом вызове.В этот момент глобальные переменные в MEX-файле получают место в памяти и инициализируются.Далее вызывается mexFunction, где ваш код получает возможность выделить память и назначить указатель на эту глобальную переменную.

При следующем вызове MEX-файла он все еще загружается в память, и этиглобальные переменные все еще там.На этот раз MATLAB просто нужно вызвать mexFunction.

Когда вы выполните clear mex или clear all в MATLAB, MEX-файл будет выгружен из памяти.Глобальные переменные перестанут существовать в этой точке.Поскольку вы использовали mxCalloc для выделения памяти, MATLAB может вернуть потерянную память.Если бы вы использовали calloc вместо этого, вы бы потеряли память в этот момент.

При следующем вызове MEX-файла это будет так же, как при первом вызове.


Обратите внимание, что, поскольку MEX-файл является скомпилированным двоичным файлом, имена ваших переменных больше не отображаются (за исключением информации отладки).Машинный код имеет дело только с адресами и регистрами памяти.

В случае 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-файла, но не указывается в памяти.Во второй раз, когда вы вызываете MEX-файл, 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;
}

В приведенном выше случае myVar будет иметь значение NULL при каждом вызове MEX-файла, так что каждый раз будет выделяться новая память.Память постоянна, поэтому в конечном итоге вам не хватит памяти с этим.

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 all или clear mex, MEX-файл будет очищен, статическая переменная myVar будет удалена, но память, выделенная malloc, все еще там.Вы снова теряете память.Если вы хотите сделать это таким образом, вам нужно зарегистрировать функцию, которая будет запускаться, когда MEX-файл существует, используя mexAtExit().

void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) 
{
    static int myVar[10] = {1};
    myVar[0] *= 2;
}

В приведенном выше случае мы используем статическую переменную дляхранить все наши данные, динамическое распределение памяти не используется, нам не нужно беспокоиться об утечке памяти.Я рекомендую это, если массив не очень большой.

1 голос
/ 14 июня 2019

Кажется, есть некоторая путаница относительно того, как mexMakeMemoryPersistent и mexMakeArrayPersistent работают, которые я хотел бы прояснить. ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Следующее основано на поведении, которое я наблюдал из тестов, которые я провел ... не обязательно в официальной документации MATLAB.

Заголовки переменных mxArray имеют поле, которое я назову VariableType, которое содержит значение, указывающее тип переменной. Например, для R2018b и ранее:

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

Для переменных в рабочей области VariableType будет 0 (нормальный). Обычно переменные, передаваемые в prhs [], будут нормальными типами.

Все (*) официальные функции API, которые выделяют память (переменные mxArray или необработанная память), помещают адрес этой памяти во временный список выделения памяти MATLAB Memory Manager для процедуры mex. ПРИМЕЧАНИЕ. Память «данных» переменной mxArray (т. Е. Вещи, стоящие за mxGetPr () и друзьями) , а не в этом списке размещения ... ее расположение полностью зависит от mxArray, это часть. VariableType для mxArrays, созданный с использованием официальных функций API, равен 4 (временно).

(*) mxArrayToString () раньше была исключением из этого, но это было исправлено в R2017a.

Когда программа mex выходит, насколько я могу судить, происходит следующее:

  1. Создаются общие копии данных переменных plhs [] (эти это то, что фактически передается обратно вызывающей стороне).

  2. Все в списках временного размещения для этой обычной процедуры уничтожен / освобожден.

На этом фоне вот что делают постоянные функции:

mexMakeMemoryPersistent (memory_address)

  • Удаляет адрес memory_address из списка временного выделения памяти

mexMakeArrayPersistent (mxArray_address)

  • Удаляет mxArray_address из временного списка размещения mxArray
  • Изменяет тип переменной mxArray за mxArray_address на 0 (нормальный)

И, фактически, документация для mexMakeMemoryPersistent гласит следующее:

"Если вы создаете постоянную память, вы несете ответственность за ее освобождение, когда очищена функция MEX. Если вы не освобождаете память, MATLAB теряет память."

Суть в том, что вы должны вручную уничтожить / освободить постоянную память ... MATLAB Memory Manager больше не поможет вам, как только вы сделаете память постоянной. Это особенно верно, когда функция mex очищается из памяти. Ваша постоянная память будет просачиваться независимо от того, что у вас была глобальная переменная, в которой она хранится, и независимо от того, что она изначально была получена из официальной функции API MATLAB. Вам нужно использовать некоторую комбинацию функций mexAtExit и / или mexLock / mexUnlock для управления этой ситуацией, чтобы не было утечки памяти. Это всегда было поведением этих постоянных функций, насколько мне известно.

СТОРОННОЕ ПРИМЕЧАНИЕ: нет официальных функций API для обратного ... то есть вы не можете сделать постоянную память непостоянной снова. Как только вы делаете что-то настойчивое, вы застряли с этим и должны справиться с этим вручную.

Демонстрация с блоком памяти 100 МБ:

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