【问题标题】:How can I ensure an emxArray is correctly allocated in generated C code?如何确保在生成的 C 代码中正确分配 emxArray?
【发布时间】:2015-06-09 22:16:12
【问题描述】:

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

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

编辑: 接受的出色答案既解决了这个问题,也解决了 (2015a) Coder 中生成的 c 代码中的一个潜在错误。在 _emxutil 文件中,如果请求的元素数量大于 intmax/2,则 emxEnsureCapacity 函数可能会出现无限循环。

【问题讨论】:

    标签: matlab memory-management matlab-coder


    【解决方案1】:

    很高兴看到您正在取得进展。关于 emxEnsureCapacity 问题,很不幸,这是一个错误。我会处理它以确保我们在即将发布的版本中修复它。同时,有一种方法可以修补生成的源代码。对于配置对象,有在生成 C 代码后执行/评估的“PostCodeGenCommand”选项。这允许您在编译之前应用补丁。例如,

    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' 的大小。

    您还可以做另一件事,但请直接与我联系(电子邮件),我会告诉您更多信息。

    亚历山大·博特玛
    软件工程师
    MATLAB 编码器团队
    数学

    【讨论】:

    • 太棒了!它回答了我最初的问题和一个小错误的问题。
    【解决方案2】:

    我已经解决了这个问题,我认为我有一个很好的解决方案。我创建了一个名为 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代码的sn-p中,我们可以看到测试:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-15
      • 1970-01-01
      • 2021-11-23
      • 2011-05-17
      • 1970-01-01
      • 2011-11-11
      • 1970-01-01
      • 2012-09-14
      相关资源
      最近更新 更多