Как я могу убедиться, что emxArray правильно размещен в сгенерированном коде C?

У меня есть следующий файл .m (с именем testmemoryallocation.m), предназначенный для генерации кода в Matlab Coder. Конечно, это всего лишь тестовый файл, демонстрирующий концепцию.

function output = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays

output = coder.nullcopy(zeros([dim, dim, dim]));
output(:) = 5;

end

Я строю это, используя следующий скрипт сборки (настройки по умолчанию из приложения Coder)

%% Create configuration object of class 'coder.CodeConfig'.
cfg = coder.config('lib','ecoder',false);
cfg.GenerateReport = true;
cfg.GenCodeOnly = true;
cfg.HardwareImplementation = coder.HardwareImplementation;
cfg.HardwareImplementation.ProdIntDivRoundTo = 'Undefined';
cfg.HardwareImplementation.TargetIntDivRoundTo = 'Undefined';

%% Define argument types for entry-point 'testmemoryallocation'.
ARGS = cell(1,1);
ARGS{1} = cell(1,1);
ARGS{1}{1} = coder.typeof(0);

%% Invoke MATLAB Coder.
codegen -config cfg testmemoryallocation -args ARGS{1}

Результирующий сгенерированный код C из этого процесса сборки выглядит следующим образом:

/*
 * testmemoryallocation.c
 *
 * Code generation for function 'testmemoryallocation'
 *
 */

/* Include files */
#include "rt_nonfinite.h"
#include "testmemoryallocation.h"
#include "testmemoryallocation_emxutil.h"

/* Function Definitions */
void testmemoryallocation(double dim, emxArray_real_T *output)
{
  int i0;
  int loop_ub;

  /* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
  i0 = output->size[0] * output->size[1] * output->size[2];
  output->size[0] = (int)dim;
  emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
  i0 = output->size[0] * output->size[1] * output->size[2];
  output->size[1] = (int)dim;
  emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
  i0 = output->size[0] * output->size[1] * output->size[2];
  output->size[2] = (int)dim;
  emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));
  loop_ub = (int)dim * (int)dim * (int)dim;
  for (i0 = 0; i0 < loop_ub; i0++) {
    output->data[i0] = 5.0;
  }
}

/* End of code generation (testmemoryallocation.c) */

Вопрос в следующем: есть ли способ в MATLAB Coder (в файле testmemoryallocation.m) проверить, что память действительно выделена? Выделение памяти происходит в функции emxEnsureCapacity, которая сама по себе не возвращает информацию об успешном выделении (насколько я могу судить). Я хотел бы иметь возможность изящно выйти из вызывающей функции, если системной памяти недостаточно чтобы завершить процесс. Например, я хотел бы добавить вывод result в testmemoryallocation, который указывал бы, произошла ли ошибка при выделении памяти. Есть ли способ сделать это?

На самом деле вопрос сводится к следующему: есть ли способ получить доступ к emxArrays, чтобы проверить, что поле data структуры не равно нулю. Возможно, файл .c называется coder.ceval? Есть ли способ указать coder.ceval передавать тип emxArray вместо базовых типов (double или int), чтобы можно было написать базовый код .c для взаимодействия со структурой?

РЕДАКТИРОВАТЬ: отличный принятый ответ касается как этого вопроса, так и основной ошибки в (2015a) Coder в сгенерированном коде c. В файле _emxutil функция emxEnsureCapacity имеет возможный бесконечный цикл, если запрошенное количество элементов превышает intmax/2.


person Tony    schedule 09.06.2015    source источник


Ответы (2)


Приятно видеть, что ты делаешь успехи. Что касается проблемы с emxEnsureCapacity, то это, к сожалению, ошибка. Я позабочусь об этом, чтобы убедиться, что мы исправим это в следующем выпуске. Между тем, есть способ исправить сгенерированный исходный код. Для объектов конфигурации есть опция «PostCodeGenCommand», которая выполняется/оценивается после того, как код C был сгенерирован. Это позволяет применить патч до того, как он будет скомпилирован. Например,

cfg.PostCodeGenCommand = 'mypatch';

а затем «mypatch.m»:

function mypatch

cfiles = all_cfiles([pwd filesep 'codegen']);
for i = 1:numel(cfiles)
    cfile = cfiles{i};
    if strcmp(cfile(end-9:end), '_emxutil.c')
        text = fileread(cfile);
        text = regexprep(text, '(while \(i < newNumel\) \{\s+i <<= 1; (\s+\})', '$1 if (i < 0) i = 0x7fffffff; $2');
        filewrite(cfile, text);
        disp(cfiles{i});
    end
end

function filewrite(filename, text)
f = fopen(filename, 'w');
fprintf(f, '%s', text);
fclose(f);

function files = all_cfiles(d)
files = {};
fs = dir(d);
for i = 1:numel(fs)
    if (fs(i).name(1) == '.')
        continue
    end
    if fs(i).isdir
        files = [files all_cfiles([d filesep fs(i).name])];
    else
        if strcmp(fs(i).name(end-1:end), '.c')
            files{end+1} = [d filesep fs(i).name];
        end
    end
end

Что касается передачи 'emxArray' внешней функции C (с coder.ref() в вызове coder.ceval()), это несколько проблематично. Это связано с тем, что представление «emxArray» может существовать или не существовать в зависимости от размера матрицы. Существует также пороговое значение (в объекте конфигурации), которое позволяет вам указать, когда переключаться на «полные» массивы emxArray или сохранять их как переменные с «выделением сверху» (которые на границах вызовов разбиваются на данные и размер как отдельные переменные). еще больше представлений для массивов переменного размера (например, если у вас есть верхние переменные в структурах). Это основная причина, по которой у нас нет прямого интерфейса для emxArrays, поскольку совместимость типов может измениться на основе этих параметров. Таким образом, всегда извлекая данные, вы сделаете их совместимыми с типом. Если вам нужно получить доступ к размеру, предполагая, что «x» здесь является вектором, вы можете просто указать размер явно:

coder.ceval('foo', x, int32(numel(x)));

К сожалению, это не позволяет вам изменить размер «x» внутри «foo».

Есть еще одна вещь, которую вы можете сделать, но, пожалуйста, свяжитесь со мной напрямую (по электронной почте), и я расскажу вам кое-что еще.

Александр Боттема
Инженер-программист
Команда MATLAB Coder
Mathworks

person Alexander Bottema    schedule 11.06.2015
comment
Превосходно! Он ответил как на мой первоначальный вопрос, так и на проблему с небольшой ошибкой. - person Tony; 11.06.2015

Я немного поработал над этой проблемой, и я думаю, что у меня есть довольно хорошее решение. Я создал функцию MATLAB с именем isallocated, которая принимает в качестве входных данных массив с плавающей запятой двойной точности переменного размера и выводит 1, если массив распределен правильно, и 0, если нет. При вызове в среде MATLAB всегда возвращает 1.

function result = isallocated(inarray) %#codegen
%ISALLOCATED Returns true if the array memory is allocated properly.
%  Will check the array to verify that the data pointer is not null.

if coder.target('MATLAB')
    result = int32(1);
    return;
end

result = int32(0);
coder.cinclude('emxArray_helpers.h');
coder.updateBuildInfo('addSourceFiles', 'emxArray_helpers.c');
result = coder.ceval('isallocated_helper', coder.ref(inarray));

end

Эта функция — просто оболочка вокруг isallocated_helper, и единственная хитрость здесь в том, что команда coder.ref извлечет указатель на массив данных в inarray. Это приводит к получению значения члена data структуры emxArray.

Функция c isallocated_helper удивительно скучна; это просто тест, чтобы увидеть, является ли указатель нулевым. Я помещу код здесь для полноты:

// emxArray_helpers.c
// A set of helper functions for emxArray types

int isallocated_helper(double * arrayptr)
{
    if (arrayptr)
        return 1;
    return 0;
}

И это все. Это хороший тест, и если я изменю файл testmemoryallocation.m, чтобы использовать его, это будет выглядеть так:

function [result, output] = testmemoryallocation(dim) %#codegen
%TESTMEMORYALLOCATION Tests allocation of large 3D arrays

output = coder.nullcopy(zeros([dim, dim, dim]));
result = isallocated(output);
if result == 1
    output(:) = 5;
else
    output = zeros([1,1,2]);
end

end

Во фрагменте сгенерированного кода на языке C мы видим тест:

...
/* TESTMEMORYALLOCATION Tests allocation of large 3D arrays */
i0 = output->size[0] * output->size[1] * output->size[2];
output->size[0] = (int)dim;
output->size[1] = (int)dim;
output->size[2] = (int)dim;
emxEnsureCapacity((emxArray__common *)output, i0, (int)sizeof(double));

/* ISALLOCATED Returns true if the array memory is allocated properly. */
/*   Will check the array to verify that the data pointer is not null.   */
*result = isallocated_helper(&output->data[0]);
if (*result == 1) {
...
person Tony    schedule 10.06.2015