MATLAB занимает много времени после последней строки функции

У меня есть функция, которая занимает много времени. Когда я его просматриваю, я обнаружил, что в течение половины времени (26 из 50 секунд) не учитывается в разбивке по строкам, и я могу показать, что время тратится после завершения работы функции, но до того, как она вернет управление следующим способом:

ts1 = tic;
disp ('calling function');
functionCall(args);
disp (['control returned to caller - ', num2str(toc(ts1))]); 

Первая строка вызываемой функции ts2 = tic, а последняя строка

disp (['last line of function- ', num2str(toc(ts2))]);

Результат

вызывающая функция

последняя строка функции - 24.0043

управление возвращается вызывающему абоненту - 49.857

Нажав на interwebs, я думаю, что это симптом того, как MATLAB управляет памятью. Он освобождает от возврата функции, а иногда это занимает много времени. Функция выделяет несколько больших (~ 1 миллион элементов) массивов. Он также работает с дескрипторами, но не создает никаких новых объектов дескриптора или хранит дескрипторы явно. Мои вопросы:

  • Это определенно проблема управления памятью?
  • Есть ли какой-либо систематический способ диагностики того, что вызывает проблему в этой функции, в отличие от других, которые быстро возвращаются?
  • Существуют ли общие советы по сокращению времени, которое MATLAB проводит на выходе функции?

Ответ 1

Я обнаружил исправление моей конкретной проблемы, которое может быть применимо в целом.

Функция, которая занимала много времени для выхода, вызывалась на базовый объект, содержащий вектор объектов handle. Когда я изменил определение базового объекта для расширения дескриптора, я устранил задержку при закрытии функции.

То, что я считаю, происходит так: когда я передал базовый объект моей функции, он создал копию этого объекта (по умолчанию MATLAB передает значение по значению). Это не занимает много времени, но когда функция завершилась, она уничтожила экземпляр объекта, что заставило его просмотреть вектор объектов дескриптора, чтобы убедиться, что не осталось никаких сирот, которые нужно было очистить. Я считаю, что именно эта операция долгое время принимала MATLAB.

Когда я сменил объект, который я передал в дескриптор, в рабочей области функции не было копии, поэтому в конце не требуется очистка объекта.

Это предлагает мне общее правило:

Если функция занимает много времени, чтобы очистить рабочее пространство при выходе, и вы передаете большое количество данных или сложных структур по значению, попробуйте инкапсулировать аргументы этой функции в объект handle

Это позволит избежать дублирования и, следовательно, отнимать много времени при выходе. Недостатком является то, что ваша функция теперь может неожиданно изменять ваши входы, потому что MATLAB не имеет возможности объявить аргумент const, как в С++.

Ответ 2

Вы правы, это время тратится на сбор мусора. Я боюсь, что это фундаментальная ошибка MATLAB, она известна с тех пор, но MathWorks не решила ее даже в новейшей версии MATLAB версии 2010b.

Вы можете попробовать вручную установить переменные [] перед тем, как покинуть функцию, т.е. вручную выполнить сборку мусора. Этот метод также помогает против памяти утечки в предыдущих версиях MATLAB. Теперь MATLAB проведет время не на end, а на myVar=[];

Вы могли бы облегчить работу без каких-либо ссылок - анонимные функции, вложенные функции, обрабатывать классы, не используя cellfun и arrayfun.

Если вы пришли к "барьеру производительности" MATLAB, возможно, вам просто нужно изменить среду. Я не вижу никакого смысла, начиная с сегодняшнего дня, нового проекта в MATLAB, за исключением случаев, когда вы используете SIMULINK. Python для технических вычислений и с С# вы также можете делать много вещей, которые MATLAB делает с помощью бесплатных библиотек. И оба являются реальными языками программирования и являются бесплатными, в отличие от MATLAB.

Ответ 3

Простым исправлением может быть следующее: предварительно распределить большие массивы и передать их в качестве аргументов в функцию functionCall(). Это переводит проблему освобождения обратно вызывающей функции functionCall(), но может случиться так, что вы вызываете functionCall чаще своего родителя, и в этом случае это ускорит ваш код.

workArr = zeros(1,1e6); % allocate once
...
functionCall(args,workArr); % call with extra argument
...
functionCall(args,wokrArr); % call again, no realloc of workArr needed
...

Внутри функцииCall вы можете позаботиться об инициализации и/или переопределении workArr, например

[workArr(:)] = 0; % reset work array