Я работал с OO MATLAB некоторое время, и в итоге посмотрел на похожие проблемы с производительностью.
Короткий ответ: да, ООП MATLAB довольно медленный. Это требует значительных накладных расходов на вызов метода, выше, чем у основных языков ОО, и вы ничего не можете с этим поделать. Частично причина может заключаться в том, что идиоматический MATLAB использует «векторизованный» код для уменьшения количества вызовов методов, и издержки на вызов не являются высоким приоритетом.
Я оценил производительность, написав бесполезные «nop» функции как различные типы функций и методов. Вот некоторые типичные результаты.
>> call_nops
Компьютер: PCWIN Релиз: 2009b
Вызов каждой функции / метода 100000 раз
Функция nop (): 0,02261 с, 0,23 мксек на вызов
Функции nop1-5 (): 0,02182 с, 0,22 мксек на вызов
Подфункция nop (): 0,02244 с, 0,22 мкс на вызов
@ () [] анонимная функция: 0,08461 сек. 0,85 пользователя на вызов
Метод nop (obj): 0,24664 сек.
nop1-5 (obj) методы: 0,23469 с 2,35 мкс на вызов
Закрытая функция nop (): 0,02197 с, 0,22 мкс на вызов
classdef nop (obj): 0,90547 сек.
classdef obj.nop (): 1.75522 сек. 17.55 usec за вызов
classdef private_nop (obj): 0,84738 сек. 8,47 чел. на вызов
classdef nop (obj) (m-файл): 0,90560 с 9,06 мкс на вызов
classdef class.staticnop (): 1,16361 с 11,64 мкс на вызов
Java nop (): 2,43035 с, 24,30 мкс на вызов
Java static_nop (): 0,87682 сек. 8,77 чел. На вызов
Java nop () из Java: 0,00014 сек.
MEX mexnop (): 0,11409 с, 1,14 мкс на вызов
C nop (): 0,00001 с, 0,00 мксек за вызов
Аналогичные результаты на R2008a через R2009b. Это на Windows XP x64 под управлением 32-битной MATLAB.
«Java nop ()» - это неиспользуемый Java-метод, вызываемый из цикла M-кода, и включает накладные расходы на отправку MATLAB-to-Java при каждом вызове. «Java nop () из Java» - это то же самое, что вызывается в цикле Java for (), и этот штраф за границы не влечет за собой. Возьмите тайминги Java и C с небольшим количеством соли; умный компилятор может полностью оптимизировать вызовы.
Механизм определения объема пакета является новым и представлен примерно в то же время, что и классы classdef. Его поведение может быть связано.
Несколько предварительных выводов:
- Методы медленнее, чем функции.
- Методы нового стиля (classdef) работают медленнее, чем методы старого стиля.
- Новый
obj.nop()
синтаксис медленнее, чем nop(obj)
синтаксис, даже для того же метода объекта classdef. То же самое для объектов Java (не показано). Если хочешь идти быстро, звони nop(obj)
.
- Затраты на вызов метода выше (примерно в 2 раза) в 64-битной среде MATLAB в Windows. (Не показаны.)
- Отправка метода MATLAB медленнее, чем в некоторых других языках.
Сказать, почему это так, было бы спекуляцией с моей стороны. ОО механизма 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 отсутствует, и картина производительности изменилась достаточно, чтобы обновить это.
Арка: PCWIN Релиз: 2011b
Машина: R2011b, Windows XP, 8x Core i7-2600 @ 3.40 ГГц, 3 ГБ оперативной памяти, NVIDIA NVS 300
Делая каждую операцию 100000 раз
общий стиль, мкс на звонок
Функция nop (): 0,01578 0,16
nop (), 10-кратное развертывание цикла: 0,01477 0,15
nop (), 100x развертка цикла: 0,01518 0,15
nop () подфункция: 0,01559 0,16
@ () [] анонимная функция: 0,06400 0,64
Метод nop (obj): 0,28482 2,85
nop () частная функция: 0,01505 0,15
classdef nop (obj): 0,43323 4,33
classdef obj.nop (): 0.81087 8.11
classdef private_nop (obj): 0.32272 3.23
classdef class.staticnop (): 0.88959 8.90
постоянная класса: 1.51890 15.19
Свойство classdef: 0,12992 1,30
Свойство classdef с геттером: 1.39912 13.99
Функция + pkg.nop (): 0,87345 8,73
+ pkg.nop () изнутри + pkg: 0.80501 8.05
Java obj.nop (): 1,86378 18,64
Java nop (obj): 0,22645 2,26
Java feval ('nop', obj): 0.52544 5.25
Java Klass.static_nop (): 0,35357 3,54
Java obj.nop () из Java: 0,00010 0,00
MEX mexnop (): 0,08709 0,87
C nop (): 0,00001 0,00
j () (встроенный): 0,00251 0,03
Я думаю, что результатом этого является то, что:
- Методы MCOS / classdef работают быстрее. Стоимость теперь примерно на одном уровне с классами старого стиля, если вы используете
foo(obj)
синтаксис. Таким образом, в большинстве случаев скорость метода больше не является причиной для использования классов старого стиля. (Слава, MathWorks!)
- Помещение функций в пространства имен делает их медленными. (Не новый в R2011b, просто новый в моем тесте.)
Обновление: R2014a
Я восстановил код тестирования и запустил его на R2014a.
Matlab R2014a на PCWIN64
Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 на PCWIN64 Windows 7 6.1 (eilonwy-win7)
Машина: Core i7-3615QM CPU @ 2,30 ГГц, 4 ГБ ОЗУ (виртуальная платформа VMware)
nIters = 100000
Время работы (мкс)
функция nop (): 0,14
nop () подфункция: 0,14
@ () [] анонимная функция: 0,69
Метод nop (obj): 3.28
nop () приватный fcn на @class: 0.14
classdef nop (obj): 5.30
classdef obj.nop (): 10,78
classdef pivate_nop (obj): 4.88
classdef class.static_nop (): 11.81
константа класса: 4.18
Свойство classdef: 1,18
Свойство classdef с геттером: 19.26
Функция + pkg.nop (): 4,03
+ pkg.nop () изнутри + pkg: 4.16
feval ('nop'): 2,31
февал (@nop): 0,22
eval ('nop'): 59,46
Java obj.nop (): 26,07
Java nop (obj): 3,72
Java feval ('nop', obj): 9,25
Java Klass.staticNop (): 10,54
Java obj.nop () из Java: 0,01
MEX mexnop (): 0,91
встроенный J (): 0,02
Доступ к полю struct s.foo: 0,14
isempty (постоянный): 0,00
Обновление: R2015b: Объекты стали быстрее!
Вот результаты R2015b, любезно предоставленные @Shaked. Это большое изменение: ООП значительно быстрее, и теперь obj.method()
синтаксис такой же быстрый method(obj)
и намного быстрее, чем унаследованные объекты ООП.
Matlab R2015b на PCWIN64
Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 на PCWIN64 Windows 8 6.2 (с нанизанным током)
Машина: Core i7-4720HQ CPU @ 2,60 ГГц, 16 ГБ ОЗУ (20378)
nIters = 100000
Время работы (мкс)
Функция nop (): 0,04
nop () подфункция: 0,08
@ () [] анонимная функция: 1,83
Метод nop (obj): 3.15
nop () приватный fcn на @class: 0.04
classdef nop (obj): 0,28
classdef obj.nop (): 0,31
classdef pivate_nop (obj): 0,34
classdef class.static_nop (): 0,05
постоянная класса: 0,25
Свойство classdef: 0,25
Свойство classdef с геттером: 0,64
Функция + pkg.nop (): 0,04
+ pkg.nop () изнутри + pkg: 0,04
feval ('nop'): 8,26
февал (@nop): 0,63
eval ('nop'): 21,22
Java obj.nop (): 14.15
Java nop (obj): 2,50
Java feval ('nop', obj): 10.30
Java Klass.staticNop (): 24,48
Java obj.nop () из Java: 0,01
MEX mexnop (): 0,33
встроенный j (): 0,15
Доступ к полю struct s.foo: 0,25
isempty (постоянный): 0,13
Обновление: R2018a
Вот результаты R2018a. Это не огромный скачок, который мы увидели, когда новый двигатель исполнения был представлен в R2015b, но это все же заметное улучшение по сравнению с прошлым годом. Примечательно, что дескрипторы анонимных функций стали намного быстрее.
Matlab R2018a на MACI64
Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 на MACI64 Mac OS X 10.13.5 (eilonwy)
Машина: Core i7-3615QM CPU @ 2,30 ГГц, 16 ГБ ОЗУ
nIters = 100000
Время работы (мкс)
Функция nop (): 0,03
nop () подфункция: 0,04
@ () [] анонимная функция: 0,16
classdef nop (obj): 0,16
classdef obj.nop (): 0,17
classdef pivate_nop (obj): 0,16
classdef class.static_nop (): 0,03
постоянная класса: 0,16
Свойство classdef: 0,13
Свойство classdef с геттером: 0,39
Функция + pkg.nop (): 0,02
+ pkg.nop () изнутри + pkg: 0,02
feval ('nop'): 15,62
февал (@nop): 0,43
eval ('nop'): 32,08
Java obj.nop (): 28,77
Java nop (obj): 8,02
Java feval ('nop', obj): 21,85
Java Klass.staticNop (): 45,49
Java obj.nop () из Java: 0,03
MEX mexnop (): 3,54
встроенный j (): 0,10
Доступ к полю struct s.foo: 0,16
isempty (постоянный): 0,07
Обновление: R2018b и R2019a: без изменений
Никаких существенных изменений. Я не удосужился включить результаты испытаний.
Исходный код для тестов
Я поместил исходный код для этих тестов в GitHub, выпущенный под лицензией MIT. https://github.com/apjanke/matlab-bench