Как стать автором
Обновить

MatLab и ООП, оптимизация

Время на прочтение3 мин
Количество просмотров20K
Данный пост предназначен для людей, имеющих практику программирования ООП (извините за тавтологию), волей судеб вынужденых писать на MatLab. Язык приятный, но граблей достаточно большое количество, и не обязательно каждому наступить на них все.

На написание вдохновил недавний пост об оптимизации кода на MatLab, точнее комментарии, требовавшие большего углубления в тему.

Итак…

История


В MatLab ООП долгое время вообще не было.
Году в 2005 появились первые, страшно выглядящие потуги: класс — папка, метод — отдельный файл, свойства — единый метод доступа с параметром «имя свойства».
«Прекрасное» начало, к счастью от него быстро отказались.

Ещё через несколько лет сделали ООП в более привычном виде — классы, наследование, всё как у людей. Кроме нескольких деталей.

Несколько деталей


Overload

До сих пор невозможно определить несколько методов с одним именем. Сам MatLab объясняет это отсутствием типизации переменных (единственное отличие разных методов было бы количество параметров) и необязательность передачи параметров в функции (приехали).

Выход — писать множество функций с похожими именами, возможно вместе с функцией-диспетчером, анализирующую ситуацию и вызывающую одну из этих функций.

Abstract static

Невозможно определить abstract static методы или свойства. А иногда очень хочется.

Выхода пока не нашёл, MatLab в курсе, багом не считает.

Передача параметров по значению или по ссылке

Все параметры передаются по значению (что заставляет многих пользователей ратовать за отход от использования функций — ужас, ужас!), но через lazy method. То есть копирование происходит только в случае изменения переменной. Если этот механизм хорошо понимать, можно выиграть (точнее не потерять по сравнению с привычной передачей переменных по ссылке) в скорости вычислений.

Объекты «обычных» классов также передаются по значению.
Впрочем, если класс наследует от стандартного MatLab класса handle, его объекты станут передаваться по ссылке.

Генерируемая документация

После java/C++ привыкаешь, что документация должна автоматически создаваться на основе оставленных тобою в коде комментариев. MatLab предлагает мутанта под названием Publisher, который шатко-валко справляется с задачей на уровне скриптов. Помимо того, что он не работает с классами, замечу факт включения в документацию всех комментариев, а не только тех, которые я хотел бы. Что означает как отказ от комментариев для себя («А вот эту функцию срочно переписать!»), а также от очень удобной системы навигации в коде по cells, которая тоже почему-то завязана на синтаксисе комментариев.

К счастью умельцы прикрутили perl-script, конвертирующий MatLab-классы в C++ (только декларирование и комментарии, естественно), что позволяет использовать «стандартный» DOxygen со всеми его прелестями — внутренние ссылки, изображения, LaTeX и пр.

Проект (я к нему отношения не имею, но рекламирую) доступен на MatLab Central.

Скорость в ООП

При переходе от обычного скрипта/функции к классу я однажды обнаружил падение производительности в 40 раз. Простое copy-paste с парочкой причёсываний, чтобы было похоже на класс — и всё, код становится в 40 раз медленнее.

Покопавшись, обнаружил, что MatLab тормозит во время доступа к переменным класса из методов того же класса. Возьмём следующий класс, единственный метод которого в разы медленнее аналогичной функции:
classdef Toto < handle
    properties
        RatingDepart
        TauxRecouv
        Weight
        FluxScenario
    end
    methods
        function obj = CollectionTaux2()
        end
        function corrigerFluxScenario(obj, Survie, CoefRedGov, CoefRedCredit)
            nbScenario = size(obj.FluxScenario, 3);
            for i = 1 : nbScenario
               for j = 1 : numel(obj.Weight)
                    obj.FluxScenario(i, j, :) = repmat(obj.Weight(j), 1, nbScenario) .* ...
                        reshape(obj.FluxScenario(i, j, :), 1, nbScenario) .* ...
                        (CoefRedGov' .* obj.TauxRecouv(j) + ...
                        CoefRedCredit(:, obj.RatingDepart(j))' .* ...
                        Survie(i, :, obj.RatingDepart(j)) .* (1 - obj.TauxRecouv(j)));
               end
            end
        end
    end
end


MatLab считает это «разумным overhead» при переходе на ООП. То есть на каждой итерации они проверяют, не изменилась ли переменная каким-то сторонним процессов, имеем ли мы до сих пор доступ к ней (как будто кто-то реально может написать такой morphe-код на MatLab) и т.п. «полезные» операции.

После долгого разговора с несколькими вменяемыми людьми MatLab вроде как согласился подумать над более приличной реализацией.

В ожидании чуда выход — определять на входе в метод локальные переменные:

        function corrigerFluxScenario(obj, Survie, CoefRedGov, CoefRedCredit)
            localFluxScenario = obj.FluxScenario;
            localWeight = obj.Weight;
            localRatingDepart = obj.RatingDepart;
            localTauxRecouv = obj.TauxRecouv;
            nbScenario = size(localFluxScenario, 3);
            for i = 1 : nbScenario
               for j = 1 : numel(localWeight)
                    localFluxScenario(i, j, :) = repmat(localWeight(j), 1, nbScenario) .* ...
                        reshape(localFluxScenario(i, j, :), 1, nbScenario) .* ...
                        (CoefRedGov' .* localTauxRecouv(j) + ...
                        CoefRedCredit(:, localRatingDepart(j))' .* ...
                        Survie(i, :, localRatingDepart(j)) .* (1 - localTauxRecouv(j)));
               end
            end
            obj.FluxScenario = localFluxScenario;
        end


Потеря производительности по сравнению со скриптом становится пренебрежимо малой.
Теги:
Хабы:
Всего голосов 23: ↑21 и ↓2+19
Комментарии22

Публикации