如何确保在生成的C代码中正确分配emxArray?

时间:2015-06-09 22:16:12

标签: matlab memory-management matlab-coder

我有以下.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函数中,该函数本身不返回有关成功分配的信息(据我所知)。如果没有&#39;,我希望能够优雅地退出调用函数。足够的系统内存来完成这个过程。例如,我想在testmemoryallocation中添加一个result输出,指示是否发生了分配内存的错误。有没有办法做到这一点?

真的,问题归结为:有没有办法访问emxArrays来测试结构的data字段不为空。也许用coder.ceval调用的.c文件?有没有办法指示coder.ceval传递emxArray类型而不是基类型(double或int),以便可以编写底层.c代码以与结构交互?

编辑: 接受的优秀答案解决了这个问题以及生成的c代码中(2015a)Coder中的底层错误。在_emxutil文件中,如果请求的元素数高于intmax/2,则emxEnsureCapacity函数可能存在无限循环。

2 个答案:

答案 0 :(得分:4)

很高兴看到你正在取得进步。关于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.ceval()调用中使用coder.ref()),这有点问题。这是因为'emxArray'表示可能存在也可能不存在,具体取决于矩阵的大小。还有一个阈值(在配置对象中),使您能够告诉何时切换到“完整”emxArrays或将它们保留为“上限分配”变量(在调用边界上将其拆分为数据和大小作为单独的变量。)更多可变大小数组的表示(例如,如果结构中有上限变量。)这是我们没有emxArrays直接接口的主要原因,因为类型兼容性可能会根据这些参数进行更改。因此,始终提取数据将使其符合类型。如果你需要访问这个大小,假设'x'是这里的向量,那么你可以明确地传递大小:

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

不幸的是,这不允许你改变'foo'里面'x'的大小。

你可以做另外一件事,但请直接联系我(电子邮件),我会告诉你更多。

Alexander Bottema
软件工程师
MATLAB编码器团队
Mathworks

答案 1 :(得分:0)

我已经解决了这个问题,我想我有一个很好的解决方案。我创建了一个名为isallocated的MATLAB函数,它将一个可变大小的双精度浮点数作为输入,如果数组正确分配则输出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中提取指向数据数组的指针。这具有获取emxArray结构的data成员的值的效果。

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