Я деякий час працював з OO MATLAB, і, нарешті, переглядав подібні проблеми з продуктивністю.
Коротка відповідь: так, OOP MATLAB - це щось повільно. Існує суттєвий накладний виклик методу, вищий, ніж основні мови OO, і ви не можете з цим зробити багато. Частина причини може полягати в тому, що ідіоматичний MATLAB використовує "векторизований" код для зменшення кількості викликів методів, а накладні витрати на виклик не є великим пріоритетом.
Я оцінив ефективність, написавши функції "nop" "nop" як різні типи функцій та методів. Ось кілька типових результатів.
>> call_nops
Комп'ютер: PCWIN Випуск: 2009b
Виклик кожної функції / методу 100000 разів
функція nop (): 0,02261 сек 0,23 Usec за виклик
nop1-5 () функції: 0,02182 сек 0,22 Usec за виклик
підфункція nop (): 0,02244 сек 0,22 Usec за виклик
@ () [] анонімна функція: 0,08461 сек 0,85 Usec за виклик
метод nop (obj): 0,24664 сек 2,47 Usec за дзвінок
nop1-5 (obj) методи: 0,23469 сек 2,35 Usec за виклик
приватна функція nop (): 0,02197 сек 0,22 Usec за виклик
classdef nop (obj): 0,90547 сек 9,05 Usec за дзвінок
classdef obj.nop (): 1.75522 сек 17.55 Usec за виклик
classdef private_nop (obj): 0,84738 сек 8,47 Usec за виклик
classdef nop (obj) (m-файл): 0,90560 сек 9,06 Usec за виклик
classdef class.staticnop (): 1,16361 сек 11,64 Usec за виклик
Java nop (): 2,43035 сек 24,30 Usec за виклик
Java static_nop (): 0,87682 сек 8,77 Usec за виклик
Java nop () з Java: 0,00014 сек 0,00 Usec за дзвінок
MEX mexnop (): 0.11409 сек 1.14 Usec за виклик
C nop (): 0,00001 сек 0,00 Usec за дзвінок
Аналогічні результати на R2008a через R2009b. Це на Windows XP x64 з 32-бітним MATLAB.
"Java nop ()" - це метод нічого не робиться Java, що викликається з циклу M-коду, і включає в себе диспетчер команди MATLAB-Java накладні з кожним викликом. "Java nop () від Java" - це те саме, що називається в циклі Java for () і не несе цього граничного покарання. Візьміть таймінги Java та C із зерном солі; розумний компілятор може повністю оптимізувати виклики.
Механізм оцінювання пакетів є новим, введений приблизно в той же час, що і класи classdef. Його поведінка може бути пов'язаною.
Кілька попередніх висновків:
- Методи повільніші, ніж функції.
- Методи нового стилю (classdef) більш повільні, ніж методи старого стилю.
- Новий
obj.nop()
синтаксис повільніше, ніж nop(obj)
синтаксис, навіть для того ж методу на об’єкті classdef. Те саме для об’єктів Java (не показано). Якщо ви хочете швидко поїхати, зателефонуйте nop(obj)
.
- Накладні накладні виклику методу вище (приблизно в 2 рази) у 64-розрядних MATLAB в Windows. (Не показаний.)
- Відправлення методу MATLAB відбувається повільніше, ніж деякі інші мови.
Сказати, чому це так, було б просто міркуванням з мого боку. Внутрішні положення OO двигуна MATLAB не є загальнодоступними. Це не інтерпретується проти компільованого випуску як такого - MATLAB має JIT - але більш друге введення та синтаксис MATLAB може означати більше роботи під час виконання. (Наприклад, ви не можете сказати з синтаксису самостійно, "f (x)" - це виклик функції або індекс в масив; це залежить від стану робочої області під час виконання.) Це може бути тому, що визначення класу MATLAB пов'язані у стан файлової системи таким чином, що багато інших мов 'не є.
Отже, що робити?
Ідіоматичний підхід MATLAB до цього полягає в "векторизації" вашого коду, структурувавши визначення вашого класу таким чином, що екземпляр об'єкта обертає масив; тобто кожне його поле містить паралельні масиви (в документації MATLAB називається "планарною" організацією). Замість того, щоб мати масив об'єктів, кожне з полями, що містять скалярні значення, визначає об'єкти, які самі є масивами, і має методи приймати масиви як входи та робити векторизовані дзвінки на поля та входи. Це зменшує кількість здійснених викликів методів, сподіваємось, що накладні витрати не є вузьким місцем.
Імітація класу C ++ або Java в MATLAB, ймовірно, не буде оптимальною. Класи Java / C ++, як правило, будуються таким чином, що об'єкти є найменшими будівельними блоками, настільки специфічними, як ви можете (тобто, безліччю різних класів), і ви складаєте їх у масиви, об'єкти колекції тощо та повторюєте їх за допомогою циклів. Щоб робити швидкі класи MATLAB, перетворіть цей підхід назовні. Майте великі класи, поля яких є масивами, і викликайте векторизовані методи на цих масивах.
Сенс полягає в тому, щоб організувати свій код, щоб він відповідав сильним мовам - обробці масивів, векторизованій математиці - і уникати слабких місць.
EDIT: З початкової публікації R2010b та R2011a вийшли. Загальна картина однакова, тому що дзвінки MCOS стають трохи швидшими, а дзвінки Java та старі стилі - повільніше .
EDIT: У мене раніше були деякі примітки про "чутливість до тракту" з додатковою таблицею часових викликів функцій, де на час функціонування впливало те, як налаштовано шлях Matlab, але, схоже, це було відхиленням моєї конкретної настройки мережі на час. Діаграма вище відображає часи, характерні для переваги моїх тестів у часі.
Оновлення: R2011b
EDIT (13.02.2012): R2011b вимкнений, а зображення продуктивності досить змінилося, щоб оновити це.
Арка: PCWIN Випуск: 2011b
Машина: R2011b, Windows XP, 8x Core i7-2600 @ 3.40GHz, 3 Гб оперативної пам’яті, NVIDIA NVS 300
Робити кожну операцію 100000 разів
загальний стиль мксек за виклик
функція nop (): 0,01578 0,16
nop (), розгортання циклу 10x: 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
константа classdef: 1,51890 15,19
майно classdef: 0.12992 1.30
майно classdef з Getter: 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
константа classdef: 4.18
властивість classdef: 1.18
майно classdef з Getter: 19.26
+ функція pkg.nop (): 4.03
+ pkg.nop () зсередини + 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 () від Java: 0.01
MEX mexnop (): 0,91
вбудований j (): 0,02
Доступ до польових структур в межах: 0.14
порожній (стійкий): 0,00
Оновлення: R2015b: Об'єкти стали швидшими!
Ось результати R2015b, люб’язно надані @Shaked Це велика зміна: OOP значно швидше, і тепер obj.method()
синтаксис швидший method(obj)
і набагато швидший, ніж застарілі об'єкти OOP.
Matlab R2015b на PCWIN64
Matlab 8.6.0.267246 (R2015b) / Java 1.7.0_60 на PCWIN64 Windows 8 6.2 (нанизаний)
Машина: процесор Core i7-4720HQ при 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
константа classdef: 0,25
властивість classdef: 0,25
майно classdef з Getter: 0,64
+ функція pkg.nop (): 0,04
+ pkg.nop () зсередини + 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 () від Java: 0.01
MEX mexnop (): 0,33
вбудований j (): 0,15
Доступ до польових структур Struff: 0,25
порожній (стійкий): 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 при 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
константа classdef: 0,16
властивість classdef: 0,13
майно classdef з getter: 0,39
+ функція pkg.nop (): 0,02
+ pkg.nop () зсередини + 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 () від Java: 0,03
MEX mexnop (): 3,54
вбудований j (): 0,10
Доступ до поля Strufo s.foo: 0,16
isempty (стійкий): 0,07
Оновлення: R2018b та R2019a: Без змін
Суттєвих змін немає. Я не намагаюся включати результати тесту.
Вихідний код для тестів
Я поставив вихідний код цих орієнтирів на GitHub, випущений під ліцензією MIT. https://github.com/apjanke/matlab-bench