Die MATLAB-Funktion ist beim ersten Mal langsam, danach jedoch viel schneller. Warum?

7

Ich habe eine große MATLAB-Funktionsdatei. Es erstellt zuerst eine Nullmatrix und aktualisiert dann ungefähr 70% der Zellen, indem es eine Anzahl entsprechender (langer) algebraischer Ausdrücke auswertet, die in der Funktion fest codiert sind. Sobald dies abgeschlossen ist, wird eine numerische Matrix zurückgegeben.

Die .m Datei ist ungefähr 4 MB groß (ich habe 100 dieser m. Dateien, aber das ist nicht direkt relevant). Wenn ich die Funktion das erste Mal auswerte, dauert es ungefähr 9 Sekunden, um auszuwerten. Nachfolgende Läufe dauern jedoch nur etwa 0,1 Sekunden, was mehr ist, was ich erwartet habe.

Warum dauert die erste Auswertung 9 Sekunden? Jedes Mal, wenn ich MATLAB schließe und wieder öffne, habe ich jedes Mal diese langsame erste Auswertung, wobei die nachfolgenden Läufe viel schneller sind. Warum ist das?

Der m. Datei kann unter dem folgenden öffentlichen Link gefunden werden (Sie können den Text aus dem Browser kopieren): Ссылка

Die Befehlsfenstereingabe, die Sie verwenden sollten, ist: [Test] = K_a_12_102x (414000000,11095e + 09,1,2500e-04,0,0840,0,0840,0,0240,0,0240,0,0020,0,0020,0,0,0,0,3.0397e + 08,8.9930e + 07,0, 3.0397e + 08,0,1,0702e + 08,0,0,0,0,0,097,7389,80,7355, -15,9811,391,1985, -15,9811,103,5248,20440000,0,20440000,0,06)

    
Kobs 21.10.2013, 15:55
quelle

3 Antworten

5
___ qstnhdr ___ Die MATLAB-Funktion ist beim ersten Mal langsam, danach jedoch viel schneller. Warum? ___ tag123matlab ___ MATLAB ist eine von MathWorks entwickelte Hochsprachen- und interaktive Programmierumgebung für numerische Berechnungen und Visualisierung. Fragen sollten entweder mit [tag: matlab] oder [tag: oktave] versehen werden, aber nicht mit beiden, es sei denn, die Frage bezieht sich explizit auf beide Pakete. Wenn Sie dieses Tag verwenden, erwähnen Sie bitte die MATLAB-Version, mit der Sie arbeiten (z. B. R2017a). ___ answer19499811 ___

Zusätzlich zu dem Effekt des JIT-Compilers, der von TheCrumbMonster erwähnt wird, könnte es verschiedene Caching -Effekte geben auf. Entweder ist Matlab selbst intelligent genug, um einige seiner Datenstrukturen wiederzuverwenden, oder es hat einen Teil seines Codes bereits im Cache-Speicher des Prozessors gespeichert und nicht im Hauptspeicher. Tatsächlich ist sogar das JIT selbst auf das Zwischenspeichern des Ergebnisses der Kompilierung angewiesen, andernfalls müssten Sie jedes Mal neu kompilieren, wenn Sie eine Funktion aufrufen. Außerdem verwenden alle modernen Betriebssysteme verschiedene Arten von Caching. Anstatt also einige Datendateien, MEX-Dateien oder DLLs von der Festplatte zu lesen, holen Sie sie einfach aus dem Speicher.

Dies ist einer der Gründe, warum Sie zur genauen Messung der Ausführungsgeschwindigkeit einiger Funktionen keine einfachen %code% -Anweisungen verwenden sollten, sondern eine Funktion wie timeit (benutze es, es ist exzellent!). Dies wiederholt eine Messung mehrmals, um den Cache aufzuwärmen und verwirft immer die ersten Messungen.

Was den Grund dafür angeht, warum Matlab für diese bestimmte Datei langsam ist, kann ich es vollkommen verstehen. Es dauert mehr als eine Minute, um meinen Texteditor zu öffnen, und Sie haben etwa 100 Zeilen mit extrem langen Anweisungen des Formulars

%Vor%

welche (hoffentlich) automatisch generiert wurden. Es scheint mir, dass die Berechnung viel effizienter durchgeführt werden kann. Zuallererst scheinen Sie nur einen sehr kleinen Teil Ihrer Matrix zu füllen, also sollten Sie wahrscheinlich eine verwenden spärliche Matrix . Als nächstes, von der schnellen Inspektion, scheint jeder Ausdruck die Form %code% , die in einer vereinfachten Weise berechnet werden kann. Ich würde wetten, dass die ganze Berechnung in ein paar Zeilen durch Multiplikation und Potenzierung von nur ein paar Matrizen durchgeführt werden kann, die als Mat-Dateien gespeichert werden sollten, nicht wie eine vollständig ausgeschriebene Berechnung. Schließlich verwenden Sie auch nicht etwa die Hälfte der Eingabeargumente für die Funktion.

    
___ answer19500758 ___

Langsame Erstläufe waren lange vor der Einführung des JIT-Compilers in MATLAB und true selbst für den Fall von MEX-Dateien , auf die der JIT-Compiler nicht angewendet wurde. Wenn Sie Code zum ersten Mal ausführen, muss MATLAB diesen von der Festplatte laden, den Code analysieren (siehe Details zur Laufzeittypanalyse) und die JIT-Kompilierung anwenden, wenn es sich um eine .m-Datei handelt. Bei der Ausführung wird Speicherplatz für die Daten zugewiesen, und die Anweisungen werden in den CPU-Cache geladen, wo sie möglicherweise für sehr schnelle Zugriffszeiten für weitere Ausführungen verbleiben. Dies ist der Grund für allgegenwärtige "Cache-Erwärmungs" -Prozeduren außerhalb der Welt von MATLAB, wie ich es verstehe (Entschuldigung für Hardware-Buffs für meine Handbewegung). Bei Festplatten ist jedoch der Festplattenzugriff wahrscheinlich ein großer Faktor, selbst bei Dateien, die viel kleiner als "ungefähr 4 MB groß" sind, wie in Ihrem Fall. Es gibt auch einen zusätzlichen Schritt der Funktion Disambiguierung, wenn mehrere Funktionen haben der gleiche Name .

Um dies für eine MEX-Datei zu sehen, führen Sie einfach %code% aus und starten Sie einen Funktionsaufruf. MATLAB muss es von der Festplatte und wieder in den Speicher laden, wahrscheinlich mit einem ungültigen CPU-Cache.

Laufzeit-Typanalyse

Ein zweiter Aspekt der Code-Beschleunigungsfunktionen (JIT-Code-Generierung ist der erste) ist die Laufzeit-Typ-Analyse. Aus einem alten MathWork Whitepaper:

  

Die Analyse des Laufzeittyps basiert auf folgender Prämisse: Wenn zuvor eine Zeile mit M-Code verarbeitet wurde, ist es sehr wahrscheinlich, dass die Variablen die gleichen Typen und Formen haben, die sie beim letzten Mal hatten Linie. Wenn eine Codezeile zum ersten Mal ausgeführt wird, untersucht das System die Variablen und generiert spezifischen Code für die gefundenen Datentypen und Formen. Weitere Ausführungen der Linie können diesen Code wiederverwenden, solange das System überprüft, dass sich die Variablentypen und -größen nicht geändert haben. Da sich die Typen selten ändern, werden nachfolgende Ausführungen so schnell wie möglich ausgeführt. Wenn sich die Typen ändern, wird der Code erneut generiert.

Sie könnten diesen Teil des JIT-Kompilierungsprozesses in Erwägung ziehen. Aber der Punkt ist, dass diese Analyse bei der ersten Ausführung ausgeführt wird, unabhängig davon, ob der Beschleuniger entscheidet, irgendwelche Codezeilen JIT-compilieren. BTW, die ganze Datei wird nicht in Maschinencode kompiliert. Früher war es möglich welche Zeilen im Profiler mit %code% beschleunigt wurden , aber das wurde leider entfernt als ein Merkmal.

Wie auch immer, nachdem Ihren Code tatsächlich betrachtet hat gibt es eine erschreckende Anzahl von Konstanten und Variablen, die nach dem Laden der Datei von der Festplatte analysiert werden müssen. Eine Zeile ist über 31.000 Zeichen lang mit mehreren tausend numerischen Literalen! Das ist eine Menge zu analysieren und zu entscheiden, was Kompilierung benötigt und was zwischen den Läufen gepuffert werden kann. Um diesen Punkt zu demonstrieren, wurde der Code nur angezeigt (nicht ausgeführt), um den Editor mit %code% auf dem Stack-Trace zum Absturz zu bringen. Yikes, das ist ein böser Code!

Generiert der JIT-Compiler Code für diese Funktion?

Lassen Sie uns den Code mit den aktivierten MATLAB-Beschleunigungsfunktionen einstellen. Wir führen auch einen Kontrolltest durch, von dem wir wissen, dass er ungefähr 8x langsamer ohne Beschleunigung läuft.

%Vor%

Jetzt schalten wir die Beschleunigung aus und führen dieselben Tests durch:

%Vor%

Ergebnisse

Die Ergebnisse sind zweifach:

  1. Die Erstlaufzeit wird NICHT verbessert, wenn die Beschleunigung (JIT) deaktiviert ist (13,28 s mit JIT on vs. 15,63 sec mit JIT off).
  2. Nachfolgende Läufe zeigen, dass kein Maschinencode generiert wird, wenn JIT aktiviert ist (0,1518 Sekunden mit JIT ON vs. 0,1597 Sekunden mit JIT aus)

Kurz gesagt, Ihr Code profitiert nicht von der JIT-Beschleunigung, und die JIT-Ausführung / -Analyse fügt NICHT die Ausführungszeit für die erste Ausführung hinzu.

Bleibt die Frage, was die langsame Erstlaufzeit verursacht? Einige Möglichkeiten: Laden des Code-Textes von der Festplatte, Parsen (Entfernen von Kommentaren und Leerzeichen) des Codes vor dem Speichern im RAM, nicht wiederverwendete Variableninitialisierung aus früheren Läufen, vielleicht MATLAB-Kernanweisungen, die von der im CPU-Cache gespeicherten Funktion verwendet werden Nicht-JIT-bezogene Codeanalyse, die für MATLAB erforderlich ist, um die Syntaxprüfung der Laufzeit durchzuführen. Die Tatsache, dass die Datei 4 MB groß und unglaublich komplex in Bezug auf die Länge der Gleichung und die schiere Anzahl der numerischen Literale ist, legt nahe, dass es sich nicht um einen CPU-Cache handelt, sondern um das erste Laden von Dateien und Codeanalyse.

    
___ tag123benchmarking ___ Benchmarking ist der Prozess des Vergleichens von zwei oder mehr Systemen oder Prozessen unter kontrollierten Bedingungen, um ein quantitatives Maß zu haben, mit dem sie verglichen oder eingestuft werden können. Das Benchmarking-Tag sollte für Fragen zur Durchführung von Benchmarking-Aufgaben oder theoretischen Fragen verwendet werden, nicht für Listen von Benchmarking-Ergebnissen oder Anfragen für Benchmarking-Daten; Diese Fragen sind für Stack Overflow off-topic. ___ antwort19499422 ___

Ich denke, das ist die JIT-Compilation. Wenn Sie die Datei zum ersten Mal ausführen, muss MATLAB sie interpretieren (Text in Maschinencode übersetzen).

Nachfolgende Läufe verwenden den zwischengespeicherten Maschinencode und werden viel schneller ausgeführt. Um dies zu überprüfen: Nach einer kleinen Änderung im Code muss MATLAB diese Kompilierung wiederholen - so sollte es für den nächsten Lauf wieder langsam sein. (Ich habe das mit genau dieser Konsequenz gemacht.) Sie machen eine beträchtliche Menge einfacher Operationen, die ziemlich schnell ausgeführt werden sollten. Die Umwandlung in Maschinencode verlangsamt Sie.

Um die Sache zu beschleunigen: Übertragen Sie den Code in C, C # oder ähnliches und fügen Sie ihn als DLL-Datei ein. Sie werden konstante und schnelle Berechnungen haben, aber Sie können sie nicht so einfach ändern.

(Mit einer C # DLL-Datei haben Sie auch eine JIT-Kompilation, aber sie ist weniger oft und wahrscheinlich noch schneller als MATLAB.)

Ich habe etwas programmiert und den Code in C # portiert. Die ursprünglichen Timings sind 13,4 s und 0,15 s (erster / zweiter Lauf)

Einfacher Port, Release Konfiguration:

%Vor%

Der erste Lauf ist also viel schlimmer als MATLAB - bummer. Obwohl ich C # so liebe, wie es ist, ist es vielleicht nicht das beste Werkzeug für diesen Job. (Übrigens: Ich musste den Befehlszeilencompiler verwenden, da Visual Studio 2010 immer wieder abstürzte ...)

    
___ qstntxt ___

Ich habe eine große MATLAB-Funktionsdatei. Es erstellt zuerst eine Nullmatrix und aktualisiert dann ungefähr 70% der Zellen, indem es eine Anzahl entsprechender (langer) algebraischer Ausdrücke auswertet, die in der Funktion fest codiert sind. Sobald dies abgeschlossen ist, wird eine numerische Matrix zurückgegeben.

Die .m Datei ist ungefähr 4 MB groß (ich habe 100 dieser m. Dateien, aber das ist nicht direkt relevant). Wenn ich die Funktion das erste Mal auswerte, dauert es ungefähr 9 Sekunden, um auszuwerten. Nachfolgende Läufe dauern jedoch nur etwa 0,1 Sekunden, was mehr ist, was ich erwartet habe.

Warum dauert die erste Auswertung 9 Sekunden? Jedes Mal, wenn ich MATLAB schließe und wieder öffne, habe ich jedes Mal diese langsame erste Auswertung, wobei die nachfolgenden Läufe viel schneller sind. Warum ist das?

Der m. Datei kann unter dem folgenden öffentlichen Link gefunden werden (Sie können den Text aus dem Browser kopieren): Ссылка

Die Befehlsfenstereingabe, die Sie verwenden sollten, ist: [Test] = K_a_12_102x (414000000,11095e + 09,1,2500e-04,0,0840,0,0840,0,0240,0,0240,0,0020,0,0020,0,0,0,0,3.0397e + 08,8.9930e + 07,0, 3.0397e + 08,0,1,0702e + 08,0,0,0,0,0,097,7389,80,7355, -15,9811,391,1985, -15,9811,103,5248,20440000,0,20440000,0,06)

    
___
DasKrümelmonster 21.10.2013, 16:04
quelle
14

Langsame Erstläufe waren lange vor der Einführung des JIT-Compilers in MATLAB und true selbst für den Fall von MEX-Dateien , auf die der JIT-Compiler nicht angewendet wurde. Wenn Sie Code zum ersten Mal ausführen, muss MATLAB diesen von der Festplatte laden, den Code analysieren (siehe Details zur Laufzeittypanalyse) und die JIT-Kompilierung anwenden, wenn es sich um eine .m-Datei handelt. Bei der Ausführung wird Speicherplatz für die Daten zugewiesen, und die Anweisungen werden in den CPU-Cache geladen, wo sie möglicherweise für sehr schnelle Zugriffszeiten für weitere Ausführungen verbleiben. Dies ist der Grund für allgegenwärtige "Cache-Erwärmungs" -Prozeduren außerhalb der Welt von MATLAB, wie ich es verstehe (Entschuldigung für Hardware-Buffs für meine Handbewegung). Bei Festplatten ist jedoch der Festplattenzugriff wahrscheinlich ein großer Faktor, selbst bei Dateien, die viel kleiner als "ungefähr 4 MB groß" sind, wie in Ihrem Fall. Es gibt auch einen zusätzlichen Schritt der Funktion Disambiguierung, wenn mehrere Funktionen haben der gleiche Name .

Um dies für eine MEX-Datei zu sehen, führen Sie einfach clear mex aus und starten Sie einen Funktionsaufruf. MATLAB muss es von der Festplatte und wieder in den Speicher laden, wahrscheinlich mit einem ungültigen CPU-Cache.

Laufzeit-Typanalyse

Ein zweiter Aspekt der Code-Beschleunigungsfunktionen (JIT-Code-Generierung ist der erste) ist die Laufzeit-Typ-Analyse. Aus einem alten MathWork Whitepaper:

  

Die Analyse des Laufzeittyps basiert auf folgender Prämisse: Wenn zuvor eine Zeile mit M-Code verarbeitet wurde, ist es sehr wahrscheinlich, dass die Variablen die gleichen Typen und Formen haben, die sie beim letzten Mal hatten Linie. Wenn eine Codezeile zum ersten Mal ausgeführt wird, untersucht das System die Variablen und generiert spezifischen Code für die gefundenen Datentypen und Formen. Weitere Ausführungen der Linie können diesen Code wiederverwenden, solange das System überprüft, dass sich die Variablentypen und -größen nicht geändert haben. Da sich die Typen selten ändern, werden nachfolgende Ausführungen so schnell wie möglich ausgeführt. Wenn sich die Typen ändern, wird der Code erneut generiert.

Sie könnten diesen Teil des JIT-Kompilierungsprozesses in Erwägung ziehen. Aber der Punkt ist, dass diese Analyse bei der ersten Ausführung ausgeführt wird, unabhängig davon, ob der Beschleuniger entscheidet, irgendwelche Codezeilen JIT-compilieren. BTW, die ganze Datei wird nicht in Maschinencode kompiliert. Früher war es möglich welche Zeilen im Profiler mit setpref('profiler','showJitLines',1); beschleunigt wurden , aber das wurde leider entfernt als ein Merkmal.

Wie auch immer, nachdem Ihren Code tatsächlich betrachtet hat gibt es eine erschreckende Anzahl von Konstanten und Variablen, die nach dem Laden der Datei von der Festplatte analysiert werden müssen. Eine Zeile ist über 31.000 Zeichen lang mit mehreren tausend numerischen Literalen! Das ist eine Menge zu analysieren und zu entscheiden, was Kompilierung benötigt und was zwischen den Läufen gepuffert werden kann. Um diesen Punkt zu demonstrieren, wurde der Code nur angezeigt (nicht ausgeführt), um den Editor mit DirectUI::DUIXmlParser::InitializeParserFromXmlLiteReader auf dem Stack-Trace zum Absturz zu bringen. Yikes, das ist ein böser Code!

Generiert der JIT-Compiler Code für diese Funktion?

Lassen Sie uns den Code mit den aktivierten MATLAB-Beschleunigungsfunktionen einstellen. Wir führen auch einen Kontrolltest durch, von dem wir wissen, dass er ungefähr 8x langsamer ohne Beschleunigung läuft.

%Vor%

Jetzt schalten wir die Beschleunigung aus und führen dieselben Tests durch:

%Vor%

Ergebnisse

Die Ergebnisse sind zweifach:

  1. Die Erstlaufzeit wird NICHT verbessert, wenn die Beschleunigung (JIT) deaktiviert ist (13,28 s mit JIT on vs. 15,63 sec mit JIT off).
  2. Nachfolgende Läufe zeigen, dass kein Maschinencode generiert wird, wenn JIT aktiviert ist (0,1518 Sekunden mit JIT ON vs. 0,1597 Sekunden mit JIT aus)

Kurz gesagt, Ihr Code profitiert nicht von der JIT-Beschleunigung, und die JIT-Ausführung / -Analyse fügt NICHT die Ausführungszeit für die erste Ausführung hinzu.

Bleibt die Frage, was die langsame Erstlaufzeit verursacht? Einige Möglichkeiten: Laden des Code-Textes von der Festplatte, Parsen (Entfernen von Kommentaren und Leerzeichen) des Codes vor dem Speichern im RAM, nicht wiederverwendete Variableninitialisierung aus früheren Läufen, vielleicht MATLAB-Kernanweisungen, die von der im CPU-Cache gespeicherten Funktion verwendet werden Nicht-JIT-bezogene Codeanalyse, die für MATLAB erforderlich ist, um die Syntaxprüfung der Laufzeit durchzuführen. Die Tatsache, dass die Datei 4 MB groß und unglaublich komplex in Bezug auf die Länge der Gleichung und die schiere Anzahl der numerischen Literale ist, legt nahe, dass es sich nicht um einen CPU-Cache handelt, sondern um das erste Laden von Dateien und Codeanalyse.

    
chappjc 21.10.2013 17:15
quelle
3

Zusätzlich zu dem Effekt des JIT-Compilers, der von TheCrumbMonster erwähnt wird, könnte es verschiedene Caching -Effekte geben auf. Entweder ist Matlab selbst intelligent genug, um einige seiner Datenstrukturen wiederzuverwenden, oder es hat einen Teil seines Codes bereits im Cache-Speicher des Prozessors gespeichert und nicht im Hauptspeicher. Tatsächlich ist sogar das JIT selbst auf das Zwischenspeichern des Ergebnisses der Kompilierung angewiesen, andernfalls müssten Sie jedes Mal neu kompilieren, wenn Sie eine Funktion aufrufen. Außerdem verwenden alle modernen Betriebssysteme verschiedene Arten von Caching. Anstatt also einige Datendateien, MEX-Dateien oder DLLs von der Festplatte zu lesen, holen Sie sie einfach aus dem Speicher.

Dies ist einer der Gründe, warum Sie zur genauen Messung der Ausführungsgeschwindigkeit einiger Funktionen keine einfachen tic(); toc() -Anweisungen verwenden sollten, sondern eine Funktion wie timeit (benutze es, es ist exzellent!). Dies wiederholt eine Messung mehrmals, um den Cache aufzuwärmen und verwirft immer die ersten Messungen.

Was den Grund dafür angeht, warum Matlab für diese bestimmte Datei langsam ist, kann ich es vollkommen verstehen. Es dauert mehr als eine Minute, um meinen Texteditor zu öffnen, und Sie haben etwa 100 Zeilen mit extrem langen Anweisungen des Formulars

%Vor%

welche (hoffentlich) automatisch generiert wurden. Es scheint mir, dass die Berechnung viel effizienter durchgeführt werden kann. Zuallererst scheinen Sie nur einen sehr kleinen Teil Ihrer Matrix zu füllen, also sollten Sie wahrscheinlich eine verwenden spärliche Matrix . Als nächstes, von der schnellen Inspektion, scheint jeder Ausdruck die Form h_a * x^n1 * const * L1^n2 * L2^n3 * .... , die in einer vereinfachten Weise berechnet werden kann. Ich würde wetten, dass die ganze Berechnung in ein paar Zeilen durch Multiplikation und Potenzierung von nur ein paar Matrizen durchgeführt werden kann, die als Mat-Dateien gespeichert werden sollten, nicht wie eine vollständig ausgeschriebene Berechnung. Schließlich verwenden Sie auch nicht etwa die Hälfte der Eingabeargumente für die Funktion.

    
Bas Swinckels 21.10.2013 16:23
quelle

Tags und Links