Я экспериментировал с MATLAB объектно - ориентированного программирования , как начать я имитировал мой С ++ классов Logger и я помещаю все мои строки вспомогательные функции в классе струнных, думая , что это было бы здорово , чтобы быть в состоянии делать такие вещи , как a + b
, a == b
, a.find( b )
вместо того strcat( a b )
, strcmp( a, b )
, получить первый элемент strfind( a, b )
и т. д.
Проблема: замедление
Я использовал вышеперечисленные вещи и сразу заметил резкое замедление. Я делаю это неправильно (что, безусловно, возможно, поскольку у меня довольно ограниченный опыт работы с MATLAB), или ООП MATLAB просто вносит много накладных расходов?
Мой тест
Вот простой тест, который я сделал для строки, просто добавив строку и снова удалив добавленную часть:
Примечание: на самом деле не пишите такой класс String в реальном коде! Matlab теперь имеет собственный
string
тип массива, и вы должны использовать его вместо этого.
classdef String < handle
....
properties
stringobj = '';
end
function o = plus( o, b )
o.stringobj = [ o.stringobj b ];
end
function n = Length( o )
n = length( o.stringobj );
end
function o = SetLength( o, n )
o.stringobj = o.stringobj( 1 : n );
end
end
function atest( a, b ) %plain functions
n = length( a );
a = [ a b ];
a = a( 1 : n );
function btest( a, b ) %OOP
n = a.Length();
a = a + b;
a.SetLength( n );
function RunProfilerLoop( nLoop, fun, varargin )
profile on;
for i = 1 : nLoop
fun( varargin{ : } );
end
profile off;
profile report;
a = 'test';
aString = String( 'test' );
RunProfilerLoop( 1000, @(x,y)atest(x,y), a, 'appendme' );
RunProfilerLoop( 1000, @(x,y)btest(x,y), aString, 'appendme' );
Результаты
Общее время в секундах на 1000 итераций:
btest 0,550 (с String.SetLength 0,138, String.plus 0,065, String.Length 0,057)
тест 0,015
Результаты для системы ведения журнала также аналогичны: 0,1 секунды для 1000 вызовов в систему frpintf( 1, 'test\n' )
, 7 (!) Секунд для 1000 вызовов моей системы при внутреннем использовании класса String (хорошо, в нем гораздо больше логики, но для сравнения с C ++: издержки моей системы, которая использует std::string( "blah" )
и std::cout
на стороне вывода по сравнению с обычным, std::cout << "blah"
составляет порядка 1 миллисекунды.)
Это просто накладные расходы при поиске функций класса / пакета?
Поскольку MATLAB интерпретируется, он должен искать определение функции / объекта во время выполнения. Поэтому мне было интересно, что, возможно, гораздо больше накладных расходов связано с поиском функций класса или пакета по сравнению с функциями, которые находятся в пути. Я попытался проверить это, и это становится странным. Чтобы исключить влияние классов / объектов, я сравнил вызов функции в пути с функцией в пакете:
function n = atest( x, y )
n = ctest( x, y ); % ctest is in matlab path
function n = btest( x, y )
n = util.ctest( x, y ); % ctest is in +util directory, parent directory is in path
Результаты, собранные так же, как указано выше:
тест 0,004 с, 0,001 с в тесте
btest 0,060 сек, 0,014 сек в util.ctest
Итак, все ли это связано с тем, что MATLAB тратит время на поиск определений для своей реализации ООП, тогда как эти издержки отсутствуют для функций, которые находятся непосредственно на пути?
for i = 1:this.get_n_quantities() if(strcmp(id,this.get_quantity_rlz(i).get_id())) ix = i; end end
занимает 2,2 секунды, аnq = this.get_n_quantities(); a = this.get_quantity_realizations(); for i = 1:nq c = a{i}; if(strcmp(id,c.get_id())) ix = i; end end
занимает 0,01, два порядка magОтветы:
Я работал с OO MATLAB некоторое время, и в итоге посмотрел на похожие проблемы с производительностью.
Короткий ответ: да, ООП MATLAB довольно медленный. Это требует значительных накладных расходов на вызов метода, выше, чем у основных языков ОО, и вы ничего не можете с этим поделать. Частично причина может заключаться в том, что идиоматический MATLAB использует «векторизованный» код для уменьшения количества вызовов методов, и издержки на вызов не являются высоким приоритетом.
Я оценил производительность, написав бесполезные «nop» функции как различные типы функций и методов. Вот некоторые типичные результаты.
Аналогичные результаты на R2008a через R2009b. Это на Windows XP x64 под управлением 32-битной MATLAB.
«Java nop ()» - это неиспользуемый Java-метод, вызываемый из цикла M-кода, и включает накладные расходы на отправку MATLAB-to-Java при каждом вызове. «Java nop () из Java» - это то же самое, что вызывается в цикле Java for (), и этот штраф за границы не влечет за собой. Возьмите тайминги Java и C с небольшим количеством соли; умный компилятор может полностью оптимизировать вызовы.
Механизм определения объема пакета является новым и представлен примерно в то же время, что и классы classdef. Его поведение может быть связано.
Несколько предварительных выводов:
obj.nop()
синтаксис медленнее, чемnop(obj)
синтаксис, даже для того же метода объекта classdef. То же самое для объектов Java (не показано). Если хочешь идти быстро, звониnop(obj)
.Сказать, почему это так, было бы спекуляцией с моей стороны. ОО механизма MATLAB не являются публичными. Это не интерпретируемая, а скомпилированная проблема как таковая - MATLAB имеет JIT - но более слабая типизация и синтаксис MATLAB могут означать больше работы во время выполнения. (Например, из одного синтаксиса нельзя определить, является ли «f (x)» вызовом функции или индексом в массиве; это зависит от состояния рабочего пространства во время выполнения.) Это может быть из-за того, что определения классов MATLAB связаны к состоянию файловой системы так, как это делают многие другие языки.
Так что делать?
Идиоматический подход MATLAB к этому состоит в том, чтобы «векторизовать» ваш код, структурируя определения вашего класса таким образом, чтобы экземпляр объекта обернул массив; то есть каждое из его полей содержит параллельные массивы (называемые «планарной» организацией в документации MATLAB). Вместо того, чтобы иметь массив объектов, каждое из которых содержит поля со скалярными значениями, они определяют объекты, которые сами являются массивами, и имеют методы, которые принимают массивы в качестве входных данных и выполняют векторизованные вызовы полей и входных данных. Это уменьшает количество выполненных вызовов методов, надеюсь, что затраты на диспетчеризацию не являются узким местом.
Подражание классу C ++ или Java в MATLAB, вероятно, не будет оптимальным. Классы Java / C ++ обычно создаются таким образом, чтобы объекты были наименьшими строительными блоками, насколько это возможно (то есть, множество различных классов), и вы составляете их в массивы, объекты коллекций и т. Д. И перебираете их с помощью циклов. Чтобы быстро создавать классы MATLAB, выверните этот подход наизнанку. Имейте большие классы, поля которых являются массивами, и вызывайте векторизованные методы для этих массивов.
Суть в том, чтобы ваш код соответствовал сильным сторонам языка - обработке массивов, векторизованной математике - и избегал слабых мест.
РЕДАКТИРОВАТЬ: начиная с оригинального сообщения, R2010b и R2011a вышли. Общая картина такая же: вызовы MCOS становятся немного быстрее, а вызовы методов Java и старого стиля - медленнее .
РЕДАКТИРОВАТЬ: Раньше у меня были некоторые заметки о «чувствительности пути» с дополнительной таблицей времени вызовов функций, где время функций зависело от того, как был настроен путь Matlab, но, похоже, это было отклонением от моей конкретной настройки сети в время. Приведенная выше таблица отражает время, типичное для большинства моих тестов с течением времени.
Обновление: R2011b
РЕДАКТИРОВАТЬ (13.02.2012): R2011b отсутствует, и картина производительности изменилась достаточно, чтобы обновить это.
Я думаю, что результатом этого является то, что:
foo(obj)
синтаксис. Таким образом, в большинстве случаев скорость метода больше не является причиной для использования классов старого стиля. (Слава, MathWorks!)Обновление: R2014a
Я восстановил код тестирования и запустил его на R2014a.
Обновление: R2015b: Объекты стали быстрее!
Вот результаты R2015b, любезно предоставленные @Shaked. Это большое изменение: ООП значительно быстрее, и теперь
obj.method()
синтаксис такой же быстрыйmethod(obj)
и намного быстрее, чем унаследованные объекты ООП.Обновление: R2018a
Вот результаты R2018a. Это не огромный скачок, который мы увидели, когда новый двигатель исполнения был представлен в R2015b, но это все же заметное улучшение по сравнению с прошлым годом. Примечательно, что дескрипторы анонимных функций стали намного быстрее.
Обновление: R2018b и R2019a: без изменений
Никаких существенных изменений. Я не удосужился включить результаты испытаний.
Исходный код для тестов
Я поместил исходный код для этих тестов в GitHub, выпущенный под лицензией MIT. https://github.com/apjanke/matlab-bench
источник
У класса handle есть дополнительные издержки от отслеживания всех ссылок на себя в целях очистки.
Попробуйте тот же эксперимент без использования класса handle и посмотрите, каковы ваши результаты.
источник
Производительность ОО существенно зависит от используемой версии MATLAB. Я не могу комментировать все версии, но по опыту знаю, что версия 2012a значительно улучшена по сравнению с версиями 2010 года. Нет эталонов и поэтому нет цифр, чтобы представить. Мой код, написанный исключительно с использованием классов дескрипторов и написанный под 2012a, вообще не будет работать в более ранних версиях.
источник
На самом деле нет проблем с вашим кодом, но это проблема с Matlab. Я думаю, что это своего рода игра вокруг, чтобы выглядеть. Нет ничего сложнее, чем компилировать код класса. Я сделал тест с простой точкой класса (один раз как дескриптор), а другой (один раз как класс значения)
вот тест
Результаты t1 =
12.0212% Ручка
t2 =
12,0042% стоимости
t3 =
t4 =
Поэтому для эффективной работы избегайте использования ООП, а структура - хороший выбор для группировки переменных.
источник