Является ли MATLAB ООП медленным или я делаю что-то неправильно?



я экспериментирую с MATLABОП, для начала я имитировал классы логгера моего C++, и я помещаю все свои вспомогательные функции string в класс String, думая, что было бы здорово иметь возможность делать такие вещи, как a + b,a == b,
из strcat( a b ),strcmp( a, b ) извлечь первый элемент strfind( a, b ) и т. д.



проблема: замедление



я поставил выше вещи, чтобы использовать и сразу же заметил кардинальные замедление. Я делаю это неправильно (что, безусловно, возможно, поскольку у меня довольно ограниченный опыт MATLAB), или ООП 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 (со строкой.SetLength 0.138, String.плюс 0.065, строку.Длина 0.057)



atest 0.015




результаты для системы регистратора аналогичны: 0,1 секунды для 1000 вызовов
к frpintf( 1, 'testn' ), 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


результаты, собранные так же, как и выше:




atest 0.004 сек, 0.001 сек в ctest



btest 0.060 сек, 0.014 сек в util.ctest




Итак, все эти накладные расходы просто приходят из MATLAB, тратя время на поиск определений для его реализации ООП, тогда как эти накладные расходы не существуют для функций, которые находятся непосредственно в пути?

730   4  

4 ответов:

Я некоторое время работал с OO MATLAB и в конечном итоге посмотрел на аналогичные проблемы с производительностью.

короткий ответ: да, ООП MATLAB немного медленный. Существует значительная накладная стоимость вызова метода, выше, чем основные языки OO, и вы не можете с этим ничего поделать. Частично причина может заключаться в том, что idiomatic MATLAB использует "векторизованный" код для уменьшения количества вызовов методов, а накладные расходы на вызов не являются высоким приоритетом.

Я сравнил производительность при написании do-nothing" 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 64-разрядная 32-разрядная версия Матлаб.

"Java nop ()" -это метод Java do-nothing, вызываемый из цикла M-кода, и включает в себя накладные расходы MATLAB-to-Java dispatch с каждым вызовом. "Java nop () from Java" - это то же самое, что называется в цикле Java for() и не делает понесите этот пограничный штраф. Возьмите Java и C тайминги с зерном соли; умный компилятор может полностью оптимизировать вызовы.

механизм определения области пакета является новым, введенным примерно в то же время, что и классы classdef. Его поведение может быть связано.

несколько предварительных выводов:

  • методы медленнее, чем функции.
  • новый стиль (classdef) методы медленнее, чем старый стиль методы.
  • новая obj.nop() синтаксис медленнее, чем nop(obj) синтаксис, даже для одного и того же метода на объекте classdef. То же самое для объектов Java (не показано). Если вы хотите ехать быстро, звоните nop(obj).
  • накладные расходы на вызов метода выше (около 2x) в 64-битном MATLAB на Windows. (Не показанный.)
  • MATLAB метод отправки медленнее, чем некоторые другие языки.

говорить, почему это так, было бы просто спекуляцией с моей стороны. Двигатель Матлаб это ОО внутренние органы не являются публичными. Это не интерпретируемая vs скомпилированная проблема как таковая-MATLAB имеет JIT-но более свободный тип и синтаксис MATLAB могут означать больше работы во время выполнения. (Например, вы не можете сказать из одного синтаксиса, является ли "f(x)" вызовом функции или индексом в массив; это зависит от состояния рабочей области во время выполнения.) Это может быть связано с тем, что определения классов MATLAB привязаны к состоянию файловой системы таким образом, что многие другие языки не являются.

Итак, что делать?

An идиоматический подход MATLAB к этому заключается в" векторизации "вашего кода путем структурирования определений классов таким образом, что экземпляр объекта обертывает массив; то есть каждое из его полей содержит параллельные массивы (называемые" планарной " организацией в документации MATLAB). Вместо того, чтобы иметь массив объектов, каждый с полями, содержащими скалярные значения, определяют объекты, которые сами являются массивами, и имеют методы, которые принимают массивы в качестве входных данных и выполняют векторизованные вызовы полей и входных данных. Это уменьшает количество из сделанных вызовов методов, надеюсь, достаточно, чтобы накладные расходы на отправку не были узким местом.

имитация класса C++ или Java в MATLAB, вероятно, не будет оптимальной. Классы Java/C++ обычно строятся таким образом, что объекты являются наименьшими строительными блоками, насколько это возможно (то есть много разных классов), и вы составляете их в массивах, объектах коллекции и т. д. и перебираете их с циклами. Чтобы сделать быстрые классы MATLAB, поверните этот подход наизнанку. Есть большие классы чьи поля являются массивами и вызывают векторизованные методы на этих массивах.

дело в том, чтобы организовать ваш код, чтобы играть в сильные стороны языка-обработка массива, векторизованная математика-и избегать слабых мест.

EDIT: начиная с исходного сообщения, R2010b и R2011a вышли. Общая картина такая же, с вызовами MCOS становится немного быстрее, а вызовы Java и старого стиля метода получают медленнее.

EDIT: раньше у меня были некоторые Примечания здесь о "чувствительности пути" с дополнительной таблицей таймингов вызова функций, где время функции зависело от того, как был настроен путь Matlab, но это, по-видимому, было аберрацией моей конкретной настройки сети в то время. Приведенная выше диаграмма отражает время, типичное для преобладания моих тестов с течением времени.

Обновление: R2011b

EDIT (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

Я думаю, что результат этого таков:

  • MCOS/classdef методы быстрее. Стоимость теперь примерно наравне с классами старого стиля, Если вы используете foo(obj) синтаксис. Таким образом, скорость метода больше не является причиной придерживаться старых классов стиля в большинстве случаев. (Спасибо, Еда!)
  • размещение функций в пространствах имен делает их медленными. (Не новый в 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 

исходный код для тестирования

Я поставил исходный код для этих тестов на GitHub, выпущенный под лицензией MIT. https://github.com/apjanke/matlab-bench

класс дескриптора имеет дополнительные накладные расходы от отслеживания всех ссылок на себя для целей очистки.

попробуйте тот же эксперимент без использования класса handle и посмотрите, каковы ваши результаты.

производительность OO значительно зависит от используемой версии MATLAB. Я не могу комментировать все версии, но знаю по опыту, что 2012a значительно улучшен по сравнению с версиями 2010 года. Нет ориентиров и поэтому нет цифр, чтобы представить. Мой код, написанный исключительно с использованием классов дескрипторов и написанный под 2012a, вообще не будет работать под более ранними версиями.

на самом деле нет проблем с вашим кодом, но это проблема с Matlab. Я думаю, что в этом есть своего рода игра вокруг, чтобы выглядеть. Это не что иное, как накладные расходы на компиляцию кода класса. Я сделал тест с простой точкой класса (один раз как дескриптор) и другой (один раз как класс значения)

    classdef Pointh < handle
    properties
       X
       Y
    end  
    methods        
        function p = Pointh (x,y)
            p.X = x;
            p.Y = y;
        end        
        function  d = dist(p,p1)
            d = (p.X - p1.X)^2 + (p.Y - p1.Y)^2 ;
        end

    end
end

вот тест

%handle points 
ph = Pointh(1,2);
ph1 = Pointh(2,3);

%values  points 
p = Pointh(1,2);
p1 = Pointh(2,3);

% vector points
pa1 = [1 2 ];
pa2 = [2 3 ];

%Structur points 
Ps.X = 1;
Ps.Y = 2;
ps1.X = 2;
ps1.Y = 3;

N = 1000000;

tic
for i =1:N
    ph.dist(ph1);
end
t1 = toc

tic
for i =1:N
    p.dist(p1);
end
t2 = toc

tic
for i =1:N
    norm(pa1-pa2)^2;
end
t3 = toc

tic
for i =1:N
    (Ps.X-ps1.X)^2+(Ps.Y-ps1.Y)^2;
end
t4 = toc

результаты t1 =

12.0212 % ручка

t2 =

12.0042 % стоимости

t3 =

0.5489  % vector

t4 =

0.0707 % structure 

поэтому для эффективной работы избегайте использования ООП вместо структуры-это хороший выбор для группировки переменных

Comments

    Ничего не найдено.