Ich versuche, eine MPI-Anwendung mit einem stark asynchronen Kommunikationsmuster zu optimieren. Jeder Rang hat eine Liste der zu berechnenden Dinge und sendet nach Bedarf Nachrichten, wenn sich die Ein- oder Ausgänge auf einem anderen Rang befinden. Zusätzlich wird jeder Rang mit einem Thread versehen (derzeit mit einem Kommunikationsthread und 5 Arbeitern).
Ich habe den Code mit Timern um die verschiedenen leistungskritischen Teile des Codes herum instrumentiert, wodurch ich eine Liste von (Start-, End-, Typ-) Tripeln für jeden Thread bekomme. Auf offensichtliche Weise dargestellt, mit Zeit als horizontale Achse, Rang und Faden als vertikale Achse und Farbe, die angibt, was jeder Faden gerade tut, erhalte ich ein Bild wie dieses für 16 Ränge mit 6 Fäden / Rang:
Meine Frage lautet: Welche anderen Möglichkeiten zur Visualisierung dieser Daten können dazu beitragen, Leistungsprobleme zu beheben? Hat jemand einen bevorzugten Plottyp, den er bei der Profilerstellung für asynchrone Anwendungen verwendet?
Dieser Datensatz ist insofern begrenzt, als er die Datenflussstruktur nicht kennt, aber ich möchte so viel Einblick wie möglich daraus gewinnen, bevor ich versuche, etwas Komplizierteres zu sammeln.
Das unkomprimierte Bild ist hier, falls sich jemand umschauen möchte (es konnte nicht über die normale Route hochgeladen werden). Leider akzeptiert Firefox es nicht, obwohl ich glaube, dass es gültig ist, möglicherweise weil es einfach zu groß ist.
quelle
Antworten:
Ich verbringe viel Zeit damit, parallelen Code zu schreiben und zu debuggen, sowohl mit gemeinsamem als auch mit verteiltem Speicher, aber ohne Ihr spezifisches Problem zu kennen, kann ich Ihnen nur sagen, was für mich am besten funktioniert.
Zu wissen , welche Routinen nehmen , wie viel Zeit ist eine wichtige Sache , wenn man sich die Recheneffizienz suchen, aber wenn Sie parallel Effizienz besorgt sind, sollten Sie dann mehr über besorgt sein , was Ihr Code tut , wenn es nicht jede Berechnung zu tun. Ein bisschen wie sich Sorgen zu machen, was die Kinder tun, wenn es zu leise ist ...
Da Sie einen hybriden Ansatz für gemeinsam genutzten / verteilten Speicher verwenden, wartet Ihr Code in den Leerzeichen entweder auf einen MPI-Aufruf oder auf eine Mutex- / Bedingungsvariable. Sie können diese Aufrufe auch in Timer einbinden, und das gibt Ihnen ein besseres Bild davon, was Sie verlangsamt, z. B. wenn es immer die gleiche Bedingung oder immer die gleiche ist
MPI_REDUCE
, auf der Ihre Threads hängen bleiben.Eine Software, die ich ziemlich oft benutze, ist der Intel Vtune Amplifier XE . Es hat eine nette Plot-Funktion / Option, die die Parallelität von Threads visualisiert. Das Programm zeichnet einen Plot, der Ihrem sehr ähnlich ist. Wenn ein Thread jedoch auf einen Mutex oder eine Bedingungsvariable wartet, zeichnet er eine diagonale Linie vom wartenden Thread zu dem Zeitpunkt, zu dem er zu warten begann, zu dem Thread, der den Mutex tatsächlich freigegeben oder signalisiert hat der Zustand, auf den es gewartet hat, als es freigegeben / signalisiert wurde. Dies kann ziemlich chaotisch sein, führt jedoch sofort zu Engpässen.
Schließlich sammle ich auch Massenstatistiken, z. B. für jeden Mutex / Signal / MPI-Anruf. Wie hoch waren die durchschnittlichen und maximalen Wartezeiten? Was ist das Histogramm der gesammelten Wartezeiten? Während die Handlung Ihnen einen schönen Überblick gibt, kann es ziemlich chaotisch werden, wenn es um die feinen Details geht.
Zum Schluss eine nicht zu unterschätzende Frage: Wie sammeln Sie Ihre Timings? Ist Ihr Timer nicht aufdringlich genug, um Ihren Code nicht zu beeinflussen? Ich verwende die Anzahl der CPU-Befehle, wann immer dies möglich ist, dh
RDTSC
auf x86-Architekturen. Dies fügt Ihrem Code normalerweise nur eine einzige Anweisung hinzu.quelle
constant_tsc
Flag gesetzt ist (überprüfen/proc/cpuinfo
). Wenn Sie die Sperrung jedes Threads für einen bestimmten Kern verwenden, dh jeder Thread liest immer dasselbe Register von demselben Kern. zB mitpthread_setaffinity_np
. Beachten Sie, dass letzteres Linux-spezifisch und daher nicht portabel ist.MPI_Waitsome
, können Sie dennoch aufzeichnen, welche Anfragen tatsächlich eingegangen sind und von wo. Diese Informationen können von Nutzen sein oder auch nicht ...Manchmal können Sie über eine Ressourcenanalyse auf hoher Ebene eine alternative Sicht auf Leistungsprobleme erhalten: Gibt es einen relevanten Engpass wie die Speicherbandbreite? Erledigt jeder Worker-Thread die gleiche Menge an Arbeit? Diese Daten können problemlos mit likwid-perfctr aus dem LIKWID-Tool-Suite- LIKWID-Google -Codeprojekt erfasst werden . Wenn das Profil so ist, dass viele verschiedene Hot Spots vorhanden sind, müssen Sie diese möglicherweise einzeln angehen. Abhängig von der Anzahl der verwendeten Threads / Prozesse können auch unterschiedliche Probleme auftreten.
quelle
Wenn ich ein Problem in einem Netzwerk hoch asynchroner Prozesse habe, die von Nachrichten oder Ereignissen gesteuert werden, verwende ich eine Methode, die nicht einfach, aber effektiv ist. Es umfasste das Abrufen von Protokollen mit Zeitstempel der Prozesse, das Zusammenführen dieser Protokolle zu einer gemeinsamen Zeitachse und das Verfolgen des Fortschritts einiger Nachrichten beim Auslösen von Aktivitäten sowie das Auslösen weiterer Nachrichten. Was ich suche, ist die Verzögerung zwischen dem Zeitpunkt des Empfangs einer Nachricht und dem Zeitpunkt, an dem sie bearbeitet wird, und das Verständnis des Grundes für die Verzögerung. Wenn ein Problem gefunden wird, wird es behoben und der Vorgang wiederholt. Auf diese Weise können Sie eine wirklich zufriedenstellende Leistung erzielen.
Es ist wichtig zu sehen, wie sich dies von Ansätzen unterscheidet, bei denen Sie messen, messen, messen. Das einzige, was die Messung Ihnen möglicherweise sagen kann, ist, wo Sie nicht suchen müssen. Für eine echte Leistungsoptimierung müssen die Details aus zeitlicher Sicht sorgfältig betrachtet werden. Was Sie suchen, ist nicht, wo Zeit verbracht wird, sondern wo sie unnötig verbracht wird.
Viel Glück.
quelle