Ich bin mit experimentiert MATLAB OOP , als beginne ich meine C ++ s Klassen Logger 'nachgeahmt und ich bin alle Hilfsfunktionen in einem String - Klasse meine Zeichenfolge setzen, dachte , es wäre toll , der Lage sein , Dinge zu tun , wie a + b
, a == b
, a.find( b )
statt strcat( a b )
, strcmp( a, b )
, Erstes Element von strfind( a, b )
usw. abrufen .
Das Problem: Verlangsamung
Ich nutzte die oben genannten Dinge und bemerkte sofort eine drastische Verlangsamung. Mache ich es falsch (was sicherlich möglich ist, da ich nur begrenzte MATLAB-Erfahrung habe), oder führt MATLABs OOP nur viel Overhead ein?
Mein Testfall
Hier ist der einfache Test, den ich für einen String durchgeführt habe. Im Grunde genommen habe ich nur einen String angehängt und den angehängten Teil wieder entfernt:
Hinweis: Schreiben Sie eine solche String-Klasse nicht in echtem Code! Matlab hat
string
jetzt einen nativen Array-Typ, den Sie stattdessen verwenden sollten.
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' );
Die Ergebnisse
Gesamtzeit in Sekunden für 1000 Iterationen:
btest 0.550 (mit String.SetLength 0.138, String.plus 0.065, String.Length 0.057)
mindestens 0,015
Die Ergebnisse für das Loggersystem sind ebenfalls: 0,1 Sekunden für 1000 Aufrufe an frpintf( 1, 'test\n' )
, 7 (!) Sekunden für 1000 Aufrufe an mein System, wenn die String-Klasse intern verwendet wird (OK, sie enthält viel mehr Logik, aber im Vergleich zu C ++: Der Overhead meines Systems, das verwendet std::string( "blah" )
und std::cout
auf der Ausgabeseite gegenüber der Ebene std::cout << "blah"
liegt, liegt in der Größenordnung von 1 Millisekunde.)
Ist es nur Overhead beim Nachschlagen von Klassen- / Paketfunktionen?
Da MATLAB interpretiert wird, muss es zur Laufzeit die Definition einer Funktion / eines Objekts nachschlagen. Ich habe mich also gefragt, ob das Nachschlagen von Klassen- oder Paketfunktionen möglicherweise viel mehr Aufwand erfordert als Funktionen, die sich im Pfad befinden. Ich habe versucht, dies zu testen, und es wird nur seltsamer. Um den Einfluss von Klassen / Objekten auszuschließen, habe ich den Aufruf einer Funktion im Pfad mit einer Funktion in einem Paket verglichen:
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
Ergebnisse, wie oben gesammelt:
mindestens 0,004 s, 0,001 s im ctest
btest 0,060 s, 0,014 s im Gebrauchstest
Kommt all dieser Aufwand nur von MATLAB, der Zeit damit verbringt, Definitionen für seine OOP-Implementierung nachzuschlagen, während dieser Aufwand nicht für Funktionen vorhanden ist, die sich direkt im Pfad befinden?
for i = 1:this.get_n_quantities() if(strcmp(id,this.get_quantity_rlz(i).get_id())) ix = i; end end
dauert 2,2 Sek., währendnq = this.get_n_quantities(); a = this.get_quantity_realizations(); for i = 1:nq c = a{i}; if(strcmp(id,c.get_id())) ix = i; end end
0,01 dauert, zwei Bestellungen von MagAntworten:
Ich habe eine Weile mit OO MATLAB gearbeitet und mich am Ende mit ähnlichen Leistungsproblemen befasst.
Die kurze Antwort lautet: Ja, MATLABs OOP ist ziemlich langsam. Es gibt einen erheblichen Aufwand für Methodenaufrufe, der höher ist als bei herkömmlichen OO-Sprachen, und Sie können nicht viel dagegen tun. Ein Grund dafür kann sein, dass das idiomatische MATLAB "vektorisierten" Code verwendet, um die Anzahl der Methodenaufrufe zu reduzieren, und dass der Overhead pro Anruf keine hohe Priorität hat.
Ich habe die Leistung durch Schreiben von "NOP" -Funktionen als verschiedene Arten von Funktionen und Methoden bewertet. Hier sind einige typische Ergebnisse.
Ähnliche Ergebnisse für R2008a bis R2009b. Dies ist unter Windows XP x64 mit 32-Bit-MATLAB möglich.
"Java nop ()" ist eine Nichtstun-Java-Methode, die aus einer M-Code-Schleife heraus aufgerufen wird und den Versandaufwand von MATLAB zu Java bei jedem Aufruf enthält. "Java nop () from Java" ist dasselbe, was in einer Java for () - Schleife aufgerufen wird, und verursacht keine Grenzstrafe. Nehmen Sie die Java- und C-Timings mit einem Körnchen Salz; Ein cleverer Compiler könnte die Aufrufe komplett optimieren.
Der Paket-Scoping-Mechanismus ist neu und wird ungefähr zur gleichen Zeit wie die classdef-Klassen eingeführt. Sein Verhalten kann zusammenhängen.
Einige vorläufige Schlussfolgerungen:
obj.nop()
Syntax ist langsamer als dienop(obj)
Syntax, selbst für dieselbe Methode für ein classdef-Objekt. Gleiches gilt für Java-Objekte (nicht gezeigt). Wenn Sie schnell gehen möchten, rufen Sie annop(obj)
.Zu sagen, warum das so ist, wäre nur Spekulation meinerseits. Die OO-Interna der MATLAB-Engine sind nicht öffentlich. Es ist an sich kein interpretiertes oder kompiliertes Problem - MATLAB hat eine JIT -, aber die lockerere Eingabe und Syntax von MATLAB kann zur Laufzeit mehr Arbeit bedeuten. (Zum Beispiel können Sie nicht allein anhand der Syntax erkennen, ob "f (x)" ein Funktionsaufruf oder ein Index in einem Array ist. Dies hängt vom Status des Arbeitsbereichs zur Laufzeit ab.) Dies kann daran liegen, dass die Klassendefinitionen von MATLAB verknüpft sind zum Dateisystemstatus in einer Weise, wie es viele andere Sprachen nicht sind.
Also, was tun?
Ein idiomatischer MATLAB-Ansatz besteht darin, Ihren Code zu "vektorisieren", indem Sie Ihre Klassendefinitionen so strukturieren, dass eine Objektinstanz ein Array umschließt. Das heißt, jedes seiner Felder enthält parallele Arrays (in der MATLAB-Dokumentation als "planare" Organisation bezeichnet). Anstatt ein Array von Objekten mit Feldern zu haben, die skalare Werte enthalten, definieren Sie Objekte, die selbst Arrays sind, und lassen Sie die Methoden Arrays als Eingaben verwenden und vektorisierte Aufrufe für die Felder und Eingaben ausführen. Dies reduziert die Anzahl der durchgeführten Methodenaufrufe, hoffentlich genug, dass der Versandaufwand kein Engpass ist.
Die Nachahmung einer C ++ - oder Java-Klasse in MATLAB ist wahrscheinlich nicht optimal. Java / C ++ - Klassen werden normalerweise so erstellt, dass Objekte die kleinsten Bausteine sind, so spezifisch wie möglich (dh viele verschiedene Klassen), und Sie komponieren sie in Arrays, Sammlungsobjekten usw. und durchlaufen sie mit Schleifen. Um schnelle MATLAB-Klassen zu erstellen, drehen Sie diesen Ansatz um. Haben Sie größere Klassen, deren Felder Arrays sind, und rufen Sie vektorisierte Methoden für diese Arrays auf.
Der Punkt ist, Ihren Code so anzuordnen, dass er die Stärken der Sprache ausspielt - Array-Handhabung, vektorisierte Mathematik - und die Schwachstellen vermeidet.
EDIT: Seit dem ursprünglichen Beitrag sind R2010b und R2011a herausgekommen. Das Gesamtbild ist das gleiche: MCOS-Aufrufe werden etwas schneller und Java- und Methodenaufrufe im alten Stil werden langsamer .
BEARBEITEN: Früher hatte ich hier einige Hinweise zur "Pfadempfindlichkeit" mit einer zusätzlichen Tabelle mit Funktionsaufrufzeiten, in der die Funktionszeiten durch die Konfiguration des Matlab-Pfads beeinflusst wurden. Dies scheint jedoch eine Abweichung von meinem speziellen Netzwerk-Setup bei gewesen zu sein die Zeit. Die obige Tabelle zeigt die Zeiten, die für das Überwiegen meiner Tests im Laufe der Zeit typisch sind.
Update: R2011b
BEARBEITEN (13.02.2012): R2011b ist nicht verfügbar, und das Leistungsbild hat sich ausreichend geändert, um dies zu aktualisieren.
Ich denke, das Ergebnis ist:
foo(obj)
Syntax verwenden. Daher ist die Methodengeschwindigkeit in den meisten Fällen kein Grund mehr, sich an alte Stilklassen zu halten. (Kudos, MathWorks!)Update: R2014a
Ich habe den Benchmarking-Code rekonstruiert und auf R2014a ausgeführt.
Update: R2015b: Objekte wurden schneller!
Hier sind die Ergebnisse von R2015b, freundlicherweise zur Verfügung gestellt von @Shaked. Dies ist eine große Änderung: OOP ist erheblich schneller, und jetzt ist die
obj.method()
Syntax so schnell wiemethod(obj)
und viel schneller als bei älteren OOP-Objekten.Update: R2018a
Hier sind die Ergebnisse von R2018a. Es ist nicht der große Sprung, den wir gesehen haben, als die neue Ausführungs-Engine in R2015b eingeführt wurde, aber es ist immer noch eine spürbare Verbesserung gegenüber dem Vorjahr. Insbesondere anonyme Funktionshandles wurden viel schneller.
Update: R2018b und R2019a: Keine Änderung
Keine wesentlichen Änderungen. Ich mache mir nicht die Mühe, die Testergebnisse einzubeziehen.
Quellcode für Benchmarks
Ich habe den Quellcode für diese Benchmarks auf GitHub veröffentlicht, der unter der MIT-Lizenz veröffentlicht wurde. https://github.com/apjanke/matlab-bench
quelle
Die Handle-Klasse hat einen zusätzlichen Aufwand, wenn alle Verweise zu Bereinigungszwecken auf sich selbst verfolgt werden.
Versuchen Sie dasselbe Experiment ohne Verwendung der Handle-Klasse und sehen Sie, was Ihre Ergebnisse sind.
quelle
Die OO-Leistung hängt wesentlich von der verwendeten MATLAB-Version ab. Ich kann nicht alle Versionen kommentieren, weiß aber aus Erfahrung, dass 2012a gegenüber 2010-Versionen stark verbessert wurde. Keine Benchmarks und daher keine Zahlen zu präsentieren. Mein Code, der ausschließlich mit Handle-Klassen geschrieben und unter 2012a geschrieben wurde, wird unter früheren Versionen überhaupt nicht ausgeführt.
quelle
Eigentlich kein Problem mit Ihrem Code, aber es ist ein Problem mit Matlab. Ich denke, es ist eine Art Herumspielen, um so auszusehen. Das Kompilieren des Klassencodes ist nichts anderes als Overhead. Ich habe den Test mit einem einfachen Klassenpunkt (einmal als Handle) und dem anderen (einmal als Werteklasse) durchgeführt.
Hier ist der Test
Die Ergebnisse t1 =
12,0212% Griff
t2 =
12.0042% Wert
t3 =
t4 =
Vermeiden Sie daher für eine effiziente Leistung die Verwendung von OOP. Stattdessen ist die Struktur eine gute Wahl, um Variablen zu gruppieren
quelle