Julia: Optimierung der Simulation eines einfachen dynamischen Systems

8

Ich versuche, die Simulation eines einfachen dynamischen Systems zu optimieren, in dem sich die Antwort eines Netzwerks sowie seine Parameter (Gewichte) nach einfachen linearen Gleichungen entwickeln. Die Simulation muss mehrere zehn Millionen Zeitschritte ausführen, aber die Netzwerkgröße ist normalerweise klein. Daher ist die Leistung weniger durch Matrix-Vektor-Produkte gebunden, als vielmehr durch temporäre Arrays, gebundene Überprüfungen und andere weniger sichtbare Faktoren. Da ich neu bei Julia bin, würde ich mich über Hinweise freuen, um die Leistung weiter zu optimieren.

%Vor%

Timing des Netzwerks (2.000.000 Schritte) mit

%Vor%

ergibt die Zeiten

%Vor%

Update 1

Nach dem Rat von David Sanders habe ich das devec-Makro losgeworden und die Loops notiert. Dies reduziert tatsächlich Array-Zuordnungen und steigert die Leistung um etwa 25%, hier sind die neuen Zahlen:

%Vor%

Je kleiner die Netzwerkgröße, desto größer der Boost. Eine Zusammenfassung des aktualisierten Simulationscodes finden Sie hier .

Update 2

Ein Großteil der Speicherzuordnungen ist auf die Matrix-Vektor-Produkte zurückzuführen. Um diese zu beseitigen, ersetzte ich diese Produkte durch eine In-Place-BLAS-Operation, BLAS.genv !, die Timings um weitere 25% und die Speicherzuweisung um 90% reduziert,

%Vor%

Aktualisierter Code hier .

Update 3

Das größte Rang-1-Update kann auch durch zwei Aufrufe von In-Place-BLAS-Funktionen ersetzt werden, nämlich BLAS.scal! zur Skalierung und BLAS.ger! für ein Rang-1-Update. Der Nachteil ist, dass beide Aufrufe ziemlich langsam sind, wenn mehr als ein Thread verwendet wird (Problem mit OpenBLAS?), So dass es am besten ist,

zu setzen %Vor%

Es gibt einen Zeitgewinn von 15% für eine Netzwerkgröße von 20 und einen Gewinn von 50% für ein Netzwerk der Größe 50. Es gibt keine Speicherzuweisungen mehr, und die neuen Zeiten sind

%Vor%

Auch hier finden Sie den aktualisierten Code hier .

Update 4

Ich habe ein einfaches Cython-Skript geschrieben, um die bisherigen Ergebnisse zu vergleichen. Der Hauptunterschied besteht darin, dass ich keine Aufrufe an BLAS verwende, sondern Schleifen: Das Eingeben von BLAS-Aufrufen niedriger Ebene ist ein Problem in Cython, und Aufrufe an einen Punkt haben zu viel Aufwand für kleine Netzwerkgrößen (ich habe es versucht). Zeiten sind

%Vor%

entspricht ungefähr der ursprünglichen Version (von der bis jetzt 50% abgeschabt wurden).

    
user45893 29.09.2015, 14:00
quelle

1 Antwort

5

Obwohl Sie das Paket Devectorize.jl verwenden, schlage ich vor, dass Sie alle diese vektorisierten Operationen einfach als einfache Schleifen ausschreiben. Ich erwarte, dass dies Ihnen einen signifikanten Leistungsschub geben wird.

Devectorize package ist sicherlich ein großer Beitrag, aber um die Reifen zu sehen, die es durchspringt, um die Drecksarbeit für Sie zu erledigen, können Sie so etwas tun (ein Beispiel aus dem Paket README):

%Vor%

Hier ist macroexpand eine Funktion, die Ihnen den Code mitteilt, auf den das Makro @devec sein Argument erweitert (der Code im Rest der Zeile). Ich werde die Überraschung nicht verderben, indem ich die Ausgabe hier zeige, aber es ist nicht nur die einfache for -Schleife, die Sie von Hand schreiben würden.

Außerdem weist die Tatsache, dass Sie über eine riesige Zuweisung verfügen, darauf hin, dass nicht alle Vektoroperationen korrekt behandelt werden.

Übrigens, vergessen Sie nicht, zuerst einen kleinen Lauf zu machen, damit Sie den Übersetzungsschritt nicht genau planen.

[Tangentiale Anmerkung:% exp ist die Funktion, die auf jedes Element einer Matrix die übliche Exponentialfunktion anwendet, entsprechend map(exp, a+b) . expm gibt das Exponential einer Matrix an. Es wurde davon gesprochen, solche Anwendungen von exp abzulehnen.]

    
David P. Sanders 29.09.2015, 20:57
quelle