【问题标题】:Matlab Get Property function causing performance problemsMatlab Get Property 函数导致性能问题
【发布时间】:2011-08-01 23:40:28
【问题描述】:

我在我创建的一个类中有一个属性,该属性当前具有与之关联的不必要的 get 函数。我最初让它在返回值之前做一些有用的事情,但现在我只需要属性值,所以 get 函数已经简化为:

    function value =  get.error(obj)
        value = obj.error;
    end

现在,由于这段代码完全是多余的,所以我决定一起删除 get 函数,但这会导致在我的代码中的“错误”属性所在的部分中难以置信速度变慢重复访问。

在这部分代码中,分析器没有明确说缺少 get 函数是问题的原因(它说所有时间都浪费在了'for' 循环),但是当我重新添加功能上无用的代码时,性能问题就消失了。

为什么删除这个无用的 get 函数会减慢我的代码速度?

编辑:我已经将问题隔离到足以发布一些代码示例。

这是一个有问题的方法调用的虚拟版本:

这是没有无用 getter 的代码配置文件:

还有神奇的无用吸气剂:

请注意,性能要求如下:

  1. 在计算集中使用我的“池”对象的某些属性来设置任何变量。 'error' 属性引起了我的注意,但该错误发生在相同情况下的所有属性中。

  2. 计算涉及任何内容,即使是 '.* 0' 也会导致这种减速,但设置的单个项是无减速的(例如 obj.pools(i).delta = obj.pools(i).error)

编辑 2:

这是完整的游泳池课程;也许会有所帮助:

classdef pool < handle

properties
    name;
    unit_count;
    activation;
    net_input = 0;
    %all projections now incoming
    projections = [];
    error; % same is dEd net for rbp
    target;
    clamped_error = false;
    delta;
    clamped_activation = 0; %0 no clamp, 1 soft clamp, 2 hard clamp
    copyback = false;
    copy_from;
    type = 'hidden';

    activation_history;
    error_history;

end

methods
    function obj = pool(name,count,type,copy_from)
        obj.name = name;
        assignin('caller', name, obj);
        obj.unit_count = count;
        obj.error = zeros(1,count);
        obj.target = zeros(1,count);
        obj.delta = zeros(1,count);
        obj.activation = zeros(1,count);
        obj.net_input = zeros(1,count);
        obj.activation_history(:,1) = zeros(1,count);
        obj.error_history(:,1) = zeros(1,count);
        if nargin > 2
            obj.type = type;
            if nargin == 4
                obj.clamped_activation = 2;
                obj.activation = ones(1,count)/2;
                obj.copy_from = copy_from;
                obj.copyback = true;
            end
        else
            obj.type = 'hidden';
        end
        switch obj.type
            case 'input'
                obj.clamped_activation = 2;
            case 'output'
                obj.clamped_error = true;
            case 'bias'
                obj.clamped_activation = 2;
                obj.activation = 1;
            case 'hidden'
        end

    end

    function proj = connect(obj,send_pool,proj_var)
        %see if you need a new projection or if the user provided one

        if nargin == 2
            proj = projection(obj, send_pool);
        else
            proj = proj_var;
        end
        obj.projections = [obj.projections struct('from',send_pool,'using',proj)];
    end


    function value =  get.error(obj)
        value = obj.error;
    end

end

end

【问题讨论】:

  • 令人惊讶。通常,吸气剂会使字段访问速度变慢,而不是更快。也许它正在影响 JIT 优化。您是否也可以包含显示访问权限的客户端代码 sn-p 吗?它只是在做foo.error吗?那个慢循环是在课堂内还是在课堂外?有和没有 getter 的调用速度有多快? (尝试在循环中仅对属性访问进行基准测试。)任何子类或超类? error中存储的是什么类型的值,字段属性是什么?你能用最小的类重现它吗?或者在你的课程中包含完整的源代码和缓慢的循环?
  • 对不起,我之前没有包含代码;当时功能太大而无用。我已将问题隔离为一个较小的假函数并发布了代码。
  • 奇怪。我没有线索。也许在没有吸气剂的情况下它会产生额外的“垃圾”,但我不知道为什么。您使用的是什么 Matlab 版本、操作系统和架构 (x86/x64)?
  • 我使用的是 Matlab 7.12.0 (R2011a),我相信它是最新版本。我正在运行 64 位 Windows 7 x86。奇怪的是没有getter,它应该使用默认值,所以我不知道为什么垃圾会有任何区别:(。也许声明允许一些JIT编译魔法?
  • 您的池对象还有哪些其他字段?它们中有多少数据(作为字节和 mxarray 的数量)?这些字段中有任何单元格数组吗?

标签: performance oop matlab profiling getter


【解决方案1】:

这听起来像 Matlab JIT 或解释器怪异。

您可能正在查看与 MCOS 内存管理相关的 Matlab 错误,就像在 Dataset array indexing is very slow with Statistics Toolbox 中讨论的那样。在某些情况下,在引用对象的字段时,该错误看起来会导致对象的虚假深度复制。如果池对象中有元胞数组或其他复杂结构,这可能会导致类似的行为 - 它们会被复制到新变量中,并且可能会在“结束”时显示释放成本。

我能够在几个 Matlab 版本中重现类似的令人惊讶的缓慢结果。但我无法证明与吸气剂的存在有任何区别。

文件 fooclass.m.

classdef fooclass
    properties
        error = 42;
        whatever = 42;
        delta = 1;
        bigjunk = num2cell(rand(1000,40));
        %bigjunk = rand(1000,10000);  % non-cell does not cause slowdown
    end

    methods        
%         function out = get.error(obj)
%             out = obj.error;
%         end
    end

end

文件 looper.m.

classdef looper
    properties
        pools
    end

    methods
        function obj = looper()
            obj.pools = repmat(fooclass(), [1 5]);;
        end

        function weird(obj)
        p = obj.pools;
        n = numel(p);
        for i = 1:n
            dummy = obj.pools(i).error .* 0;
        end
        end
    end
end

复制:

>> lp = looper;
>> tic; for i = 1:100; weird(lp); end; toc
Elapsed time is 0.600428 seconds.

这是在我的 64 位 Windows 7 上的 R2011a(预发行版)上。速度很慢。时间与 bigjunk 的大小成正比,分析器说这几乎都发生在 p = obj.pools 行上。这表明对象引用正在导致复制,而不是像它应该使用的那样使用写时复制优化。或者它正在遍历对象图,或者其他什么。您的代码可能会发生类似的情况。

最好将其归咎于解释器的怪异,如果它使您的代码更快,则将 getter 留在其中,然后等待下一个版本中的修复。听起来值得作为错误报告提交给 MathWorks。

【讨论】:

  • 感谢您的回答;您对问题的再现有很大帮助。事实证明这是一个释放的内存问题;如果您调用“profile -memory on”,则分析器会显示内存使用统计信息。有了这个,我可以看到我无用的 getter 很好,并在必要时释放内存,而默认的 getter 则不会。我想我会将此问题报告给 Mathworks,因为这种行为似乎是一个错误。
猜你喜欢
  • 2017-12-17
  • 2019-05-04
  • 2011-07-18
  • 1970-01-01
  • 2017-12-10
  • 2012-02-10
  • 1970-01-01
  • 2022-11-18
  • 2018-01-23
相关资源
最近更新 更多