Ответ 1
Я работал с OO MATLAB некоторое время, и в итоге посмотрел на похожие проблемы с производительностью.
Короткий ответ: да, MATLAB OOP довольно медленный. Существуют значительные накладные расходы на вызовы методов, которые выше, чем у основных языков ОО, и вы мало что можете с этим поделать. Частично причина может заключаться в том, что идиоматический MATLAB использует "векторизованный" код для уменьшения количества вызовов методов, и издержки на вызов не являются высоким приоритетом.
Я оценил производительность, написав бесполезные "nop" функции как различные типы функций и методов. Вот некоторые типичные результаты.
>> call_nops Computer: PCWIN Release: 2009b Calling each function/method 100000 times nop() function: 0.02261 sec 0.23 usec per call nop1-5() functions: 0.02182 sec 0.22 usec per call nop() subfunction: 0.02244 sec 0.22 usec per call @()[] anonymous function: 0.08461 sec 0.85 usec per call nop(obj) method: 0.24664 sec 2.47 usec per call nop1-5(obj) methods: 0.23469 sec 2.35 usec per call nop() private function: 0.02197 sec 0.22 usec per call classdef nop(obj): 0.90547 sec 9.05 usec per call classdef obj.nop(): 1.75522 sec 17.55 usec per call classdef private_nop(obj): 0.84738 sec 8.47 usec per call classdef nop(obj) (m-file): 0.90560 sec 9.06 usec per call classdef class.staticnop(): 1.16361 sec 11.64 usec per call Java nop(): 2.43035 sec 24.30 usec per call Java static_nop(): 0.87682 sec 8.77 usec per call Java nop() from Java: 0.00014 sec 0.00 usec per call MEX mexnop(): 0.11409 sec 1.14 usec per call C nop(): 0.00001 sec 0.00 usec per call
Аналогичные результаты на 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 отсутствует, и картина производительности изменилась достаточно, чтобы обновить это.
Arch: PCWIN Release: 2011b Machine: R2011b, Windows XP, 8x Core i7-2600 @ 3.40GHz, 3 GB RAM, NVIDIA NVS 300 Doing each operation 100000 times style total µsec per call nop() function: 0.01578 0.16 nop(), 10x loop unroll: 0.01477 0.15 nop(), 100x loop unroll: 0.01518 0.15 nop() subfunction: 0.01559 0.16 @()[] anonymous function: 0.06400 0.64 nop(obj) method: 0.28482 2.85 nop() private function: 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 classdef constant: 1.51890 15.19 classdef property: 0.12992 1.30 classdef property with getter: 1.39912 13.99 +pkg.nop() function: 0.87345 8.73 +pkg.nop() from inside +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() from Java: 0.00010 0.00 MEX mexnop(): 0.08709 0.87 C nop(): 0.00001 0.00 j() (builtin): 0.00251 0.03
Я думаю, что результатом этого является то, что:
- Методы MCOS/classdef работают быстрее. Стоимость теперь примерно такая же, как у классов старого стиля, если вы используете синтаксис
foo(obj)
. Таким образом, в большинстве случаев скорость метода больше не является причиной для использования классов старого стиля. (Слава, MathWorks!) - Помещение функций в пространства имен делает их медленными. (Не новый в R2011b, просто новый в моем тесте.)
Обновление: R2014a
Я восстановил код тестирования и запустил его на R2014a.
Matlab R2014a on PCWIN64 Matlab 8.3.0.532 (R2014a) / Java 1.7.0_11 on PCWIN64 Windows 7 6.1 (eilonwy-win7) Machine: Core i7-3615QM CPU @ 2.30GHz, 4 GB RAM (VMware Virtual Platform) nIters = 100000 Operation Time (µsec) nop() function: 0.14 nop() subfunction: 0.14 @()[] anonymous function: 0.69 nop(obj) method: 3.28 nop() private fcn on @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 classdef constant: 4.18 classdef property: 1.18 classdef property with getter: 19.26 +pkg.nop() function: 4.03 +pkg.nop() from inside +pkg: 4.16 feval('nop'): 2.31 feval(@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() from Java: 0.01 MEX mexnop(): 0.91 builtin j(): 0.02 struct s.foo field access: 0.14 isempty(persistent): 0.00
Обновление: R2015b: Объекты стали быстрее!
Вот результаты R2015b, любезно предоставленные @Shaked. Это большое изменение: ООП значительно быстрее, и теперь синтаксис obj.method()
такой же быстрый, как method(obj)
, и намного быстрее, чем унаследованные объекты ООП.
Matlab R2015b on PCWIN64 Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 on PCWIN64 Windows 8 6.2 (nanit-shaked) Machine: Core i7-4720HQ CPU @ 2.60GHz, 16 GB RAM (20378) nIters = 100000 Operation Time (µsec) nop() function: 0.04 nop() subfunction: 0.08 @()[] anonymous function: 1.83 nop(obj) method: 3.15 nop() private fcn on @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 classdef constant: 0.25 classdef property: 0.25 classdef property with getter: 0.64 +pkg.nop() function: 0.04 +pkg.nop() from inside +pkg: 0.04 feval('nop'): 8.26 feval(@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() from Java: 0.01 MEX mexnop(): 0.33 builtin j(): 0.15 struct s.foo field access: 0.25 isempty(persistent): 0.13
Обновление: R2018a
Вот результаты R2018a. Это не тот огромный скачок, который мы увидели, когда новый двигатель исполнения был представлен в R2015b, но это все же заметное улучшение по сравнению с прошлым годом. Примечательно, что дескрипторы анонимных функций стали намного быстрее.
Matlab R2018a on MACI64 Matlab 9.4.0.813654 (R2018a) / Java 1.8.0_144 on MACI64 Mac OS X 10.13.5 (eilonwy) Machine: Core i7-3615QM CPU @ 2.30GHz, 16 GB RAM nIters = 100000 Operation Time (µsec) nop() function: 0.03 nop() subfunction: 0.04 @()[] anonymous function: 0.16 classdef nop(obj): 0.16 classdef obj.nop(): 0.17 classdef pivate_nop(obj): 0.16 classdef class.static_nop(): 0.03 classdef constant: 0.16 classdef property: 0.13 classdef property with getter: 0.39 +pkg.nop() function: 0.02 +pkg.nop() from inside +pkg: 0.02 feval('nop'): 15.62 feval(@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() from Java: 0.03 MEX mexnop(): 3.54 builtin j(): 0.10 struct s.foo field access: 0.16 isempty(persistent): 0.07
Обновление: R2018b и R2019a: без изменений
Никаких существенных изменений. Я не потрудился включить результаты теста.
Исходный код для тестов
Я поместил исходный код для этих тестов в GitHub, выпущенный под лицензией MIT. https://github.com/apjanke/matlab-bench