MATLAB OOP est lent ou je fais quelque chose de mal?
j'expérimente avec MATLAB OOP , comme un début j'ai imité mes classes Logger DE C++et je mets toutes mes fonctions d'assistant de chaîne dans une classe de chaîne, en pensant que ce serait génial d'être capable de faire des choses comme a + b
, a == b
, a.find( b )
au lieu de cela
de strcat( a b )
, strcmp( a, b )
, récupérer le premier élément de strfind( a, b )
, etc.
le problème: ralentissement
j'ai mis les choses ci-dessus à utiliser et immédiatement remarqué un drastique ralentissement. Est-ce que je le fais mal (ce qui est certainement possible car J'ai une expérience assez limitée de MATLAB), ou est-ce que L'OOP de MATLAB introduit juste beaucoup de frais généraux?
Mon cas de test
voici le test simple que j'ai fait pour la chaîne de caractères, en ajoutant simplement une chaîne de caractères et en enlevant à nouveau la partie ajoutée:
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' );
les résultats
temps Total en secondes, pour 1000 itérations:
btest 0.550 (avec de la Ficelle.SetLength 0.138, String.plus de 0,065, Chaîne.Longueur 0,057)
au plus 0,015
résultats pour le système logger sont également: 0.1 secondes pour 1000 appels
à frpintf( 1, 'testn' )
, 7 (! secondes pour 1000 appels à mon système quand en utilisant la classe String en interne (OK, il y a beaucoup plus de logique en elle, mais à comparer avec C++: le plafond de mon système qui utilise std::string( "blah" )
et std::cout
du côté de la sortie vs plaine std::cout << "blah"
est de l'ordre de 1 milliseconde.)
Est-ce que les frais généraux lors de la recherche de classe/fonctions de package?
puisque MATLAB est interprété, il doit chercher la définition d'une fonction/objet au moment de l'exécution. J'ai donc été se demander que peut-être beaucoup plus de frais généraux est impliqué dans la recherche classe ou fonction paquet vs fonctions qui sont dans le chemin. J'ai essayé de le tester, et il est juste parfait inconnu. Pour exclure l'influence des classes / objets, j'ai comparé appeler une fonction dans le chemin vs une fonction dans un paquet:
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
résultats, recueillis de la même manière que ci-dessus:
atest 0.004 sec, 0.001 sec en ctest
btest 0.060 sec, en 0.014 s en util.ctest
donc, tous ces frais généraux viennent-ils tout juste de MATLAB passant du temps à chercher des définitions pour son implémentation OOP, alors que ces frais généraux ne sont pas là pour les fonctions qui sont directement dans le chemin?
4 réponses
j'ai travaillé avec OO MATLAB pendant un certain temps, et j'ai fini par examiner des problèmes de performance similaires.
la réponse courte est: oui, MATLAB's OOP is kind of slow. Il y a beaucoup de méthode Call overhead, plus élevé que les langues oo traditionnelles, et il n'y a pas grand chose que vous pouvez faire à ce sujet. Une partie de la raison peut être que idiomatic MATLAB utilise le code "vectorisé" pour réduire le nombre d'appels de méthode, et les frais généraux par appel n'est pas une priorité élevée.
j'ai comparé la performance en écrivant des fonctions" nop " comme les différents types de fonctions et de méthodes. Voici quelques résultats typiques.
>> 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
résultats similaires sur R2008a à R2009b. C'est sur Windows XP x64 qui exécute MATLAB 32 bits.
le "Java nop ()" est une méthode Java dite "do-nothing" appelée à partir d'une boucle de code M, et inclut le "MATLAB-to-Java dispatch overhead" avec chaque appel. "Java nop() de Java" est l' même chose appelée dans une boucle Java pour () et n'encourt pas cette pénalité de limite. Prenez les temps Java et c avec un grain de sel; un compilateur intelligent pourrait optimiser les appels complètement.
le mécanisme de détermination de la portée du paquet est nouveau, introduit à peu près en même temps que les classes classedef. Son comportement peut être lié.
quelques conclusions provisoires:
- les méthodes sont plus lentes que les fonctions.
- les méthodes new style (classdef) sont plus lentes que les méthodes old style.
- la nouvelle syntaxe
obj.nop()
est plus lente que la syntaxenop(obj)
, même pour la même méthode sur un objet classdef. Idem pour les objets Java (non représentés). Si vous voulez aller vite, appeleznop(obj)
.
L'appel de méthode - est plus élevé (environ 2x) dans le MATLAB 64 bits sur Windows. (Non illustré.)
- l'expédition par la méthode MATLAB est plus lente que d'autres langue.
dire pourquoi il en est ainsi ne serait que spéculation de ma part. Les internes du moteur MATLAB ne sont pas publics. Il ne s'agit pas d'un problème interprété par rapport à un problème compilé en soi - MATLAB a un JIT - mais la syntaxe et la dactylographie plus lâche de MATLAB peuvent signifier plus de travail au moment de l'exécution. (P. ex. vous ne pouvez pas dire à partir de la seule syntaxe si "f(x)" est un appel de fonction ou un index dans un tableau; cela dépend de l'état de l'espace de travail au moment de l'exécution.) Peut-être parce que la classe de MATLAB les définitions sont liées à l'état du système de fichiers d'une manière que beaucoup d'autres langues ne sont pas.
Alors, que faire?
une approche MATLAB idiomatique est de " vectoriser "votre code en structurant vos définitions de classe de telle sorte qu'une instance objet enveloppe un tableau; c'est-à-dire que chacun de ses champs possède des tableaux parallèles (appelé organisation" planaire " dans la documentation MATLAB). Plutôt que d'avoir un tableau d'objets, chacun avec des champs de portefeuille de valeurs scalaires, définissez des objets qui sont eux-mêmes des tableaux, et ayez les méthodes prennent des tableaux comme entrées, et font des appels vectorisés sur les champs et les entrées. Cela réduit le nombre d'appels de méthode faite, je l'espère assez que l'expédition de frais généraux n'est pas un goulot d'étranglement.
imiter une Classe C++ ou Java dans MATLAB ne sera probablement pas optimal. Les classes Java/C++ sont généralement construites de telle sorte que les objets sont les plus petits blocs de construction, aussi spécifiques que vous le pouvez (c'est-à-dire, beaucoup de classes différentes).), et vous les composez dans des tableaux, des objets de collection, etc, et itérez-les avec des boucles. Pour faire des cours MATLAB rapides, tournez cette approche à l'envers. Ont des classes plus grandes dont les champs sont des tableaux, et appellent des méthodes vectorisées sur ces tableaux.
le point est d'arranger votre code pour jouer aux forces de la manipulation du langage - tableau, mathématiques vectorisées - et d'éviter les points faibles.
EDIT: depuis le post original, R2010b et R2011a sont venus hors. L'image générale est la même, avec les appels de MCOS devenant un peu plus rapides, et les appels de méthodes Java et old-style obtenant plus lent .
EDIT: j'avais l'habitude d'avoir quelques notes ici sur la "sensibilité de chemin" avec une table supplémentaire de fonction Appel timings, où les temps de fonction ont été affectés par la façon dont le chemin de Matlab a été configuré, mais cela semble avoir été une aberration de ma configuration de réseau particulière à l'époque. Le graphique ci-dessus reflète les temps typique de la prépondérance de mes tests au fil du temps.
Mise À Jour: R2011b
MODIFIER (2/13/2012): R2011b et, à l'image de performances a suffisamment changé pour mettre à jour ce.
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
je pense que le résultat de ceci est que:
- les méthodes MCOS / classdef sont plus rapides. Le coût est maintenant à peu près équivalent avec les anciennes classes de style, tant que vous utilisez la syntaxe
foo(obj)
. Donc la vitesse de la méthode n'est pas une raison plus longue de s'en tenir aux anciennes classes de style dans la plupart des cas. (Bravo, MathWorks!) - mettre des fonctions dans les espaces de noms les rend lents. (Pas nouvelle dans R2011b, tout nouveau dans mon test.)
Mise À Jour: R2014a
j'ai reconstruit le code d'analyse comparative et je l'ai lancé sur R2014 a.
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
mise à jour : R2015b: les objets sont devenus plus rapides!
Voici les résultats R2015b, s'il vous plaît fourni par @Shaked. C'est un grand changement: OOP est beaucoup plus rapide, et maintenant la syntaxe obj.method()
est aussi rapide que method(obj)
, et beaucoup plus rapide que les objets hérités OOP.
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
Mise À Jour: R2018a
Voici les résultats de R2018a. Ce n'est pas l'énorme bond que nous avons vu lorsque le nouveau moteur d'exécution a été introduit dans R2015b, mais c'est encore une amélioration appréciable d'année en année. Notamment, fonction anonyme les poignées sont plus rapides.
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
Code Source de Repères
j'ai mis le code source de ces benchmarks sur GitHub, publié sous licence MIT. https://github.com/apjanke/matlab-bench
la classe de la poignée comporte un plafond additionnel qui permet de suivre toutes les références à elle-même à des fins de nettoyage.
essayez la même expérience sans utiliser la classe de poignée et voir ce que vos résultats sont.
OO performance dépend fortement de la MATLAB version utilisée. Je ne peux pas commenter toutes les versions, mais je sais par expérience que 2012a est bien meilleure que les versions 2010. Aucun point de référence et donc pas de chiffres à présenter. Mon code, exclusivement écrit en utilisant les classes handle et écrit sous 2012a ne sera pas exécuté du tout sous les versions précédentes.
en fait pas de problème avec votre code mais C'est un problème avec Matlab. Je pense qu'il est une sorte de jeu autour de ressembler. Ce n'est rien d'autre que des frais généraux pour compiler le code de classe. J'ai fait le test avec le point de classe simple (une fois comme poignée) et l'autre (une fois comme classe de valeur)
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
voici le test
%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
les résultats t1 =
12.0212 % de la Poignée
t2 =
12.0042% valeur
t3 =
0.5489 % vector
t4 =
0.0707 % structure
par conséquent, pour une performance efficace, éviter D'utiliser la structure OOP est un bon choix pour grouper les variables