Функция Matlab Get Property вызывает проблемы с производительностью

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

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

Теперь, видя, что этот код полностью лишний, я решил удалить функцию get целиком, но это привело к невероятному замедлению в одном разделе моего кода, где неоднократно обращались к свойству 'error'.

В этом разделе кода профилировщик не явно говорит, что причиной проблемы является отсутствующая функция get (он говорит, что все время тратится впустую в 'конце' 'for' loop), но когда я добавляю обратно функционально бесполезный код, проблема с производительностью исчезает.

Почему удаление этой бесполезной функции get замедлило бы мой код?

РЕДАКТИРОВАТЬ: я достаточно изолировал проблему, чтобы опубликовать несколько примеров кода.

Вот фиктивная версия вызова метода с проблемой: введите здесь описание изображения

Вот профиль кода без бесполезного геттера: введите здесь описание изображения

и с помощью волшебного бесполезного геттера: введите здесь описание изображения

Обратите внимание, что требования к производительности выглядят следующим образом:

  1. использовать некоторое свойство моего объекта «пул» в вычислении, установленном для любой переменной. Свойство «ошибка» привлекло мое внимание, но ошибка возникает со всеми свойствами в одной и той же ситуации.

  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

person zergylord    schedule 01.08.2011    source источник
comment
Удивительно. Обычно геттеры делают доступ к полям медленнее, а не быстрее. Возможно, это влияет на JIT-оптимизацию. Можете ли вы также включить фрагмент кода клиента, показывающий доступ? Он просто делает foo.error? Это медленный цикл внутри класса или снаружи? Насколько быстры вызовы с геттером и без него? (Попробуйте сравнить только доступ к свойствам в цикле.) Какие-нибудь подклассы или суперклассы? Какой тип значения хранится в error и каковы атрибуты поля? Можете ли вы воспроизвести его с минимальным классом? Или включить полный исходный код в свой класс и этот медленный цикл?   -  person Andrew Janke    schedule 02.08.2011
comment
Извините, я не включил код раньше; в то время функции были слишком большими, чтобы быть полезными. Я изолировал проблему от небольшой поддельной функции и опубликовал код.   -  person zergylord    schedule 02.08.2011
comment
Странный. У меня нет подсказок. Может быть, это создает лишний мусор в отсутствие геттера, но я не знаю, почему. Какую версию Matlab, ОС и архитектуру (x86/x64) вы используете?   -  person Andrew Janke    schedule 02.08.2011
comment
Я использую Matlab 7.12.0 (R2011a), который, как мне кажется, является последней версией. Я использую 64-битную Windows 7 x86. Что странно, так это отсутствие геттера, он должен использовать значение по умолчанию, поэтому я не знаю, почему будет какая-то разница в мусоре :(. Может быть, объявление допускает некоторую магию JIT-компиляции?   -  person zergylord    schedule 02.08.2011
comment
Какие еще поля у вас есть в объектах пула? Сколько в них данных (в байтах и ​​количестве mxarray)? Любые массивы ячеек в этих полях?   -  person Andrew Janke    schedule 03.08.2011
comment
Я добавил classdef к вопросу; это прилично большой класс, причем самым большим полем являются «проекции», в которых хранится массив дескрипторов классов для проекционных объектов, которые, в свою очередь, представляют собой матрицы двойников.   -  person zergylord    schedule 03.08.2011
comment
@zergylord позвольте нам продолжить обсуждение в чате   -  person Andrew Janke    schedule 03.08.2011


Ответы (1)


Это звучит как Matlab JIT или странность интерпретатора.

Возможно, вы столкнулись с ошибкой Matlab, связанной с управлением памятью MCOS, как описано в Индексирование массива данных выполняется очень медленно с помощью панели инструментов статистики. Эта ошибка выглядит так, как будто она вызывает ложное глубокое копирование объектов при ссылке на их поля в некоторых случаях. Если у вас есть массивы ячеек или другие сложные структуры в объектах пула, это может привести к аналогичному поведению — они копируются в новые переменные, и, возможно, стоимость освобождения отображается в «конце».

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

Это на моем R2011a (предварительном выпуске) в 64-битной Windows 7. Это медленно. Время пропорционально размеру большого мусора, и профилировщик говорит, что почти все это происходит на строке p = obj.pools. Это говорит о том, что ссылка на объект вызывает копирование, а не использует оптимизацию копирования при записи, как предполагалось. Или это обход графа объектов, или что-то в этом роде. Что-то подобное может происходить с вашим кодом.

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

person Andrew Janke    schedule 02.08.2011
comment
Спасибо за ответ; Ваше воспроизведение проблемы очень помогло. Оказывается, это была проблема с освобожденной памятью; если вы вызываете 'profile -memory on', то профилировщик отображает статистику использования памяти. С этим я мог видеть, что мой бесполезный геттер был хорошим и освобождал память, когда это было необходимо, в то время как геттер по умолчанию этого не делал. Я думаю, что сообщу об этой проблеме в Mathworks, так как это поведение похоже на ошибку. - person zergylord; 04.08.2011