Neuen Eigenschaften des Performance-Analysers Intel VTune Amplifier XE

Der Fokus liegt vor allem auf den neuen Eigenschaften des Performance-Analysers Intel VTune Amplifier XE und seiner Anwendung. Dieses Werkzeug ist eine konsequente Weiterentwicklung des älteren VTune Amplifiers, der vollkommen überarbeitet wurde. Die wesentlichen Neuheiten sind ein erheblich verbessertes Benutzerinterface (GUI). Dazu kommt eine Zeitrahmenfensteranalyse, die der Entwickler über ein Programminterface (API) steuern kann. Ein weiteres Feature ist die schnell arbeitende, mit CPU Zählern unterstützte Event basierende, abtastende Call Stack Tree Analyse, die durch neue Filterfunktionen begleitet wird.

In diesem Artikel soll der Fokus vor allem auf die neuen Eigenschaften des Performance-Analysers Intel VTune Amplifier XE und seine Anwendung gelenkt werden. Dieses Werkzeug ist eine konsequente Weiterentwicklung des älteren VTune Amplifiers, der vollkommen überarbeitet wurde. Die wesentlichen Neuheiten sind ein erheblich verbessertes Benutzerinterface (GUI). Dazu kommt eine Zeitrahmenfensteranalyse, die der Entwickler über ein Programminterface (API) steuern kann. Ein weiteres Feature ist die schnell arbeitende, mit CPU Zählern unterstützte Event basierende, abtastende Call Stack Tree Analyse, die durch neue Filterfunktionen begleitet wird.
Gegenüber der Vorgängerversion, bei der die Anzahl der Events begrenzt war, sind nun dank einer Multiplexertechnologie erheblich viel mehr Events pro Testlauf auswertbar. Zum Schluss sei noch erwähnt, dass neben dem C++ Quellcode auch der zugehörige Assemblercode gleichzeitig in zwei verschiedenen Fenstern angezeigt werden kann.

Die Schlüsselfunktionen des Intel Vtune Amplifier XE

Will man quasi in Echtzeit testen, darf der VTune Amplifier XE nicht fälschlicherweise als Executable mittels Debuggerinstrumentierung betrieben werden, sondern muss stattdessen als Releasecode laufen.

Hotspot-Analyse

Eine der wichtigsten Funktionen ist die Hotspotanalyse. In diesem Modus wird der Programmstatus periodisch nach einem äquidistanten Zeitrasterverfahren (Counting) abgetastet oder die Applikation durch ein Event-Filter per Interrupt unterbrochen (EBS = Event Based Sampling). In beiden Fällen wir der Status aller vordefinierten CPU Register für die spätere Anzeige und Auswertung aufgezeichnet.
Dadurch ist nur eine geringe zeitliche Beeinträchtigung des Programms (quasi Echtzeitbetrieb) gewährleistet. Da die CPU-Counter,von denen es bei den neuesten Prozessoren um die 2000 Stück gibt, muss bei jeder weiteren Version ein VTune Amplifier XE Update zur Verfügung gestellt werden. Das Auswendiglernen der Eventnamen ist aber nicht notwendig, da sie in der Hilfefunktion ausführlich beschrieben werden. In der derzeitigen ausgelieferten Version VTune Amplifier XE sind die neuen Events der Nehalem/Westmere-Architektur beinhaltet. Die Events der Sandybridge-Architektur hingegen müssen derzeit noch separat vom Netz heruntergeladen werden. Die Hotspotanalyse hilft sowohl bei der Leistungsoptimierung einer seriellen als auch einer parallelen Applikation. Nach einem Testlauf im Hotspot-Modus werden die Leistungsenpässe im GUI des VTune Amplifier XE angezeigt (Bild 1).

Bild 1: Hotspot-Analyse – Funktionen und dazugehörige CPU-Laufzeiten.

Bild 1: Hotspot-Analyse – Funktionen und dazugehörige CPU-Laufzeiten.

Je länger die blauen Querbalken sind, um so mehr Zeit wurde für die Ausführung benötigt. Klickt man auf das „+“-Kästchen sieht man die Callstacks des nächsten Levels, die auch ineinander geschachtelt sein können und fortlaufend eingerückt dargestellt werden. Man kann in diesem Bildausschnitt auch gut sehen, wie sich die benötigte CPU-Zeit einer Funktion (grid-intersect) ungleich auf zwei weitere Funktionen verteilt (intersect_objects und grid_intersect). Wobei Letztere wiederum intersect_objects nutzt. Wäre man daran interessiert, grid_intersect zu optimieren, würde man innerhalb von grid_intersect die zeitlich längste Funktion, also intersect_objects, analysieren und dessen Quellcode genauer anschauen. Will man die Hotspotanalayse zeilengenau dem C++ Sourcecode zuordnen, klickt man auf die Funktion oder das Programmmodul beziehungsweise auf den blauen Querbalken (in Bild 1) und erhält als gewünschtes Resultat die Darstellung in Bild 2.

Bild 2: Hotspotanalyse im Quellcode.

Bild 2: Hotspotanalyse im Quellcode.

Programmeffizienz

Bevor man sich der Verbesserung über Threading zuwendet, kann man den Leistungssteigerungen im seriellen Program nachgehen. Leistungssteigerungen und algorthmische Verbesserungen des seriellen kommen auch dem parallelen Programm zu gute.
Die Counterdarstellung des Intel Vtune Amplifier XE (Bild 3) zeigt Programmineffizienzen an.

Bild  3: Auffinden von Programmineffizienz auf Funktionsebene.

Bild 3: Auffinden von Programmineffizienz auf Funktionsebene.

Der Wert CPI (Clocks per Instruction) ist solch ein Indikator. Der optimal erreichbare Wert beträgt 0.25 (oder 4 „retired Instructions“ per Clock). Dies entspricht der Maximalauslastung der Core 2 Duo Architektur. CPI-Werte von über 1 (wie in Bild 3) sind ein Anzeichen von geringer Prozessorauslastung. Typische Ursachen sind Cache Misses oder Branchmispredictions. Dazu existieren ebenfalls entprechende Eventscounter, die in einem Testlauf (siehe auch Bild 3) gemessen und am besten zusammen mit dem Quellcode ausgewertet werden. Im Event Counter Mode wird farblich auf kritische (im Bild pink unterlegt) Bereiche hingewiesen, wie das oben erwähnte CPI-Beispiel zeigt.

Wartezyklenanalyse von Locks

Wenn man ein paralleles Programm untersucht, ist ausser der optimalen Nebenläufigkeit (parallele Ausnutzung der vorhandenen Prozessorkerne durch Threads) vor allem die Wartezyklenanalyse von Locks und deren Effizienz von grossem Belang. Will ein Thread A zum Beispiel auf eine globale Variable zugreifen, die von einem anderen Thread B bereits genutzt und gelocked (gesperrt) ist, muss der erste (Thread A) solange warten bis Thread B seinen Lock auf die globale Variable freigibt. Dies kann bei vielen Threads und längeren Lock-Bereichen zu einer Quasi-Serialisierung eines parallelen Programms führen. Zumindest wird der Programmablauf verlangsamt.

Bild 4: Effizenzanalyse von Locks  (Wait for Locks).

Bild 4: Effizenzanalyse von Locks (Wait for Locks).

Bild 4 zeigt qualitativ an, welche Funktionen nicht optimal mit Locking umgehen. Lange rote Balken sind ein Indiz für Lock-probleme. Auch hier kann man wieder in den Sourcecode einsteigen. Dort kann man dann eroieren, wie das Problems zu optimieren oder zu lösen ist. Eventuell hat man eine Lockingmethode mit zuviel Overhead gewählt. Eventuell ist durch Codeumstellungen wie der Nutzung privater Variablen innerhalb der verschiedenen Threads eine Vermeidung von Locks möglich. Aber auch die Nutzung von „Concurrent Containers“ wie es das Threading Building Block (TBB) Model von Intel offeriert, kann eine Lösung darstellen.

Nebenläufigkeitsanalyse der Threads

Noch effizienter ist die Nebenläufigkeitsanalyse der Threads mittels einer zeitlichen Darstellung. In Bild 5 kann man im zeitlichen Ablauf erkennen, ob ein Thread tatsächlich aktiv ist (running). Dieser Thread wird entsprechend grün hinterlegt. Vor allem ist zu sehen, wieviel CPU-Zeit benötigt wird und zwar sowohl pro Thread als auch über alle Threads zusammen (braune Histogrammdarstellung). Ein Hineinzoomen in diesen Zeitablauf, um sich Bereiche genauer anzusehen, ist möglich. Dazu wird mit der Maus der zu untersuchende Bereich markiert (wie in Bild 5 schattiert angezeigt) und dann per Doppelklick das Zoomen gestartet.

Bild 5: Thread-Analyse über die Zeitachse.

Bild 5: Thread-Analyse über die Zeitachse.

Lock-und Waits-Analyse

Eine weitere Darstellung im Zeitbereich ist die Lock-und Waits-Analyse (Bild 6).

Bild 6: Thread –Nebenläufigkeitsanalyse (Concurrency Analysis)  in Zeitdarstellung

Bild 6: Thread –Nebenläufigkeitsanalyse (Concurrency Analysis) in Zeitdarstellung

Es wir dann angezeigt, wann Threads Programm ausführen ( dunkelgrün) und wann sie im Wartemodus verharren (hellgrün). Synchornisierungspunkte zwischen den Threads werden durch gelbe Pfeile verdeutlicht. Auch hier ist eine Hineinzoomen möglich, um einen höhere Auflösung und damit bessere Deutung des parallelen Programmablaufs zu ermöglichen. Die blauen Zeitmarkierungen sind Rahmen, die der Softwareentwickler gezielt per API in sein Programm vor dem Testlauf einbauen kann. So wäre es z.B. möglich, gezielt als Rahmen, ein Bild in einem Spieleprgramm zu markieren und zu filtern. Bei Spielen ist das sinnvoll, da Einzelbilder (Frames) und for allem „Frames pro Sekunde“ ein natürlicher Performanceindikator für das Spiel ist.
Die erkannten Ineffizienzen (Load-Imbalances, Synchronisierungsoverhead und so weiter) kann man wie zum Teil durch die oben beschriebenen Ansätze oder durch optimierte Lockmechanismen verbessern.

Vergleichsmodus

Zuletzt sei noch der Vergleichsmodus erwähnt. Dieser Modus ist hilfreich für Regressionstests, die in der Regel einmal am Tag und bevorzugt über Nacht laufen. Sie lassen sich auch mit dem Kommandozeilenmodus (Command Line Mode; CLI) kombinieren und von dort aus starten CLI-Kommandos sind per „Cut und Paste“-Verfahren aus dem Amplifier XE (auch entsprechend aus dem Inspector XE) heraus einfach zu initialisieren.