Атрибуты являются, по существу, битами данных, которые Вы хотите присоединить к Вашему типы (классы, методы, события, перечисления, и т.д.)
, идея состоит в том, что во время выполнения некоторый другой тип/платформа/инструмент будет запрашивать Ваш тип для получения информации в атрибуте и реагировать на них.
Так, например, Visual Studio может запросить атрибуты на стороннем управлении для выяснения, какие свойства управления должны появиться в области Properties во время проектирования.
Атрибуты могут также использоваться в Аспектно-ориентированном программировании для введения/управления объектов во время выполнения на основе атрибутов, которые украшают их и добавляют проверку, вход, и т.д. к объектам, не влияя на бизнес-логику объекта.
Некоторое время я работал с OO MATLAB и в конце концов обнаружил похожие проблемы с производительностью.
Короткий ответ: да, ООП MATLAB работает медленно. Существуют значительные накладные расходы на вызов методов, более высокие, чем у основных объектно-ориентированных языков, и с этим мало что можно сделать. Частично причина может заключаться в том, что идиоматический 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 () from Java» - это то же самое, что вызывается в цикле Java for (), и не влечет за собой штраф за границу. Возьмите тайминги Java и C с недоверием; умный компилятор может полностью оптимизировать вызовы.
Механизм определения области видимости пакета является новым и представлен примерно в то же время, что и классы classdef. Его поведение может быть связано.
Несколько предварительных выводов:
obj.nop () Синтаксис
медленнее, чем синтаксис nop (obj)
, даже для того же метода в объекте classdef. То же самое для объектов Java (не показаны). Если вы хотите действовать быстро, позвоните по номеру nop (obj)
. Сказать, почему это так, было бы только предположением с моей стороны. Внутренние объекты OO механизма MATLAB не являются общедоступными. Сама по себе проблема не интерпретируемая и скомпилированная - в MATLAB есть JIT, - но более свободная типизация и синтаксис MATLAB могут означать больше работы во время выполнения. (Например, вы не можете определить только по синтаксису, является ли «f (x)» вызовом функции или индексом в массиве; это зависит от состояния рабочей области во время выполнения.) Это может быть потому, что определения классов MATLAB связаны в состояние файловой системы, чего не делают многие другие языки.
Итак, что делать?
Идиоматический подход MATLAB к этому - "векторизация" ваш код, структурируя определения ваших классов так, чтобы экземпляр объекта был оболочкой для массива; то есть каждое из его полей содержит параллельные массивы (называемые «плоской» организацией в документации MATLAB). Вместо того, чтобы иметь массив объектов, каждое из которых содержит поля, содержащие скалярные значения, определяют объекты, которые сами по себе являются массивами, а методы принимают массивы в качестве входных данных и выполняют векторизованные вызовы полей и входов. Это уменьшает количество выполняемых вызовов методов, достаточно надеяться, что накладные расходы на отправку не являются узким местом.
Имитация класса C ++ или Java в MATLAB, вероятно, не будет оптимальной. Классы Java / C ++ обычно строятся так, что объекты являются наименьшими строительными блоками, настолько конкретными, насколько это возможно (то есть множеством разных классов), и вы составляете их в массивы, объекты коллекции и т. Д. И перебираете их с помощью циклов. Чтобы сделать классы MATLAB быстрыми, выверните этот подход наизнанку. Создавайте классы большего размера, поля которых являются массивами, и вызывайте векторизованные методы для этих массивов.
Смысл в том, чтобы организовать ваш код таким образом, чтобы он использовал сильные стороны языка - обработку массивов, векторизованную математику - и избегал слабых мест. РЕДАКТИРОВАТЬ: С момента первоначального сообщения вышли R2010b и R2011a. Общая картина такая же: вызовы MCOS становятся немного быстрее, а вызовы методов Java и старого стиля становятся медленнее .
РЕДАКТИРОВАТЬ: Раньше у меня были некоторые заметки о «чувствительности пути» с дополнительная таблица таймингов вызовов функций, где на время функций влияло то, как был настроен путь Matlab, но, похоже, это было отклонением от моей конкретной настройки сети в то время. В приведенной выше таблице отражены времена, характерные для преобладания моих тестов с течением времени.
РЕДАКТИРОВАТЬ (2/13/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
Я думаю, что результатом этого является следующее:
foo (obj)
. Таким образом, скорость метода больше не является причиной в большинстве случаев придерживаться классов старого стиля. (Престижность, MathWorks!) Я реконструировал код тестирования и запустил его на 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, любезно предоставленные @Shaked. Это большое изменение: ООП значительно быстрее, и теперь синтаксис obj.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 . Это не тот огромный скачок, который мы наблюдали, когда в 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
Без существенных изменений. Я не утруждаю себя включением результатов тестирования.
Я поместил исходный код для этих тестов на GitHub, выпущенный под лицензией MIT. https://github.com/apjanke/matlab-bench
У класса дескриптора есть дополнительные накладные расходы, связанные с отслеживанием всех ссылок на себя в целях очистки.
Попробуйте тот же эксперимент без использования класса дескриптора и посмотрите, каковы ваши результаты.