Welche anderen Programme machen dasselbe wie gprof?
Welche anderen Programme machen dasselbe wie gprof?
Antworten:
Valgrind hat einen Befehlszähler-Profiler mit einem sehr schönen Visualisierer namens KCacheGrind . Wie Mike Dunlavey empfiehlt, zählt Valgrind den Bruchteil der Anweisungen, für die eine Prozedur auf dem Stapel ausgeführt wird, obwohl ich leider sagen muss, dass sie bei gegenseitiger Rekursion verwirrt zu sein scheint. Aber der Visualizer ist sehr schön und Lichtjahre voraus gprof
.
gprof (lesen Sie das Papier) existiert aus historischen Gründen. Wenn Sie glauben, dass es Ihnen bei der Suche nach Leistungsproblemen hilft, wurde es nie als solches beworben. Folgendes steht in der Zeitung:
Das Profil kann verwendet werden, um die Kosten verschiedener Implementierungen zu vergleichen und zu bewerten.
Es heißt nicht, dass es verwendet werden kann, um die verschiedenen zu bewertenden Implementierungen zu identifizieren , obwohl dies impliziert, dass dies unter besonderen Umständen möglich ist:
insbesondere wenn festgestellt wird, dass kleine Teile des Programms die Ausführungszeit dominieren.
Was ist mit Problemen, die nicht so lokalisiert sind? Sind die egal? Setzen Sie keine Erwartungen an gprof , die nie dafür beansprucht wurden. Es ist nur ein Messwerkzeug und nur für CPU-gebundene Operationen.
Versuchen Sie dies stattdessen.
Hier ist ein Beispiel für eine 44-fache Beschleunigung.
Hier ist eine 730-fache Beschleunigung.
Hier ist eine 8-minütige Videodemonstration.
Hier ist eine Erklärung der Statistik.
Hier ist eine Antwort auf Kritik.
Es gibt eine einfache Beobachtung über Programme. Bei einer bestimmten Ausführung ist jede Anweisung für einen Bruchteil der Gesamtzeit verantwortlich (insbesondere für call
Anweisungen), in dem Sinne, dass die Zeit nicht aufgewendet würde, wenn sie nicht vorhanden wäre. Während dieser Zeit befindet sich die Anweisung auf dem Stapel **. Wenn das verstanden wird, können Sie sehen, dass -
gprof verkörpert bestimmte Mythen über Leistung, wie zum Beispiel:
Diese Programmzählerabtastung ist nützlich.
Dies ist nur dann sinnvoll, wenn Sie einen unnötigen Hotspot-Engpass haben, z. B. eine Blasensorte mit einer großen Anzahl skalarer Werte. Sobald Sie es beispielsweise mithilfe von Zeichenfolgenvergleich in eine Sortierung ändern, ist dies immer noch ein Engpass, aber beim Programmzähler-Sampling wird dies nicht angezeigt, da sich der Hotspot jetzt im Zeichenfolgenvergleich befindet. Wenn andererseits der erweiterte Programmzähler (der Aufrufstapel) abgetastet wird, wird der Punkt, an dem der Zeichenfolgenvergleich aufgerufen wird, die Sortierschleife, klar angezeigt. Tatsächlich war gprof ein Versuch, die Einschränkungen der Nur-PC-Probenahme zu beseitigen.
Diese Timing-Funktionen sind wichtiger als das Erfassen zeitaufwändiger Codezeilen.
Der Grund für diesen Mythos ist, dass gprof keine Stapelproben erfassen konnte. Stattdessen werden Funktionen zeitlich festgelegt , ihre Aufrufe gezählt und versucht, das Aufrufdiagramm zu erfassen. Sobald jedoch eine kostspielige Funktion identifiziert wurde, müssen Sie darin nach den Zeilen suchen, die für die Zeit verantwortlich sind. Wenn es Stapelmuster gäbe, die Sie nicht suchen müssten, wären diese Zeilen auf den Beispielen. (Eine typische Funktion haben könnte 100-1000 Anweisungen Eine Funktion. Aufruf ist 1 Anweisung, so etwas , das teure Anrufe lokalisiert ist 2-3 Größenordnungen genauer.)
dass das Anrufdiagramm wichtig ist.
Was Sie über ein Programm wissen müssen, ist nicht, wo es seine Zeit verbringt, sondern warum. Wenn Zeit in einer Funktion verbracht wird, gibt jede Codezeile auf dem Stapel ein Glied in der Argumentationskette, warum sie vorhanden ist. Wenn Sie nur einen Teil des Stapels sehen können, können Sie nur einen Teil des Grundes dafür sehen, sodass Sie nicht sicher sagen können, ob diese Zeit tatsächlich erforderlich ist. Was sagt Ihnen das Anrufdiagramm? Jeder Bogen zeigt an, dass eine Funktion A für einen Bruchteil der Zeit eine Funktion B aufrief. Selbst wenn A nur eine solche Codezeile hat, die B aufruft, gibt diese Zeile nur einen kleinen Teil des Grundes dafür an. Wenn Sie Glück haben, hat diese Linie vielleicht einen schlechten Grund. Normalerweise müssen Sie mehrere Zeilen gleichzeitig sehen, um einen schlechten Grund zu finden, wenn er vorhanden ist. Wenn A B an mehr als einer Stelle anruft, sagt es Ihnen noch weniger.
Diese Rekursion ist ein heikles, verwirrendes Thema. Dies
liegt nur daran, dass gprof und andere Profiler die Notwendigkeit erkennen, einen Aufrufgraphen zu generieren und dann den Knoten Zeiten zuzuweisen . Wenn man Samples des Stapels hat, ist der Zeitaufwand für jede Codezeile, die auf Samples erscheint, eine sehr einfache Zahl - der Bruchteil der Samples, auf denen er sich befindet. Wenn es eine Rekursion gibt, kann eine bestimmte Zeile in einem Sample mehrmals vorkommen.
Ganz gleich. Angenommen, alle N ms werden Proben genommen, und die Linie erscheint auf F% von ihnen (einzeln oder nicht). Wenn diese Zeile keine Zeit in Anspruch nehmen kann (z. B. durch Löschen oder Verzweigen), verschwinden diese Stichproben und die Zeit wird um F% reduziert.
Diese Genauigkeit der Zeitmessung (und damit eine große Anzahl von Proben) ist wichtig.
Denken Sie eine Sekunde darüber nach. Wenn sich eine Codezeile auf 3 von fünf Samples befindet, können Sie sie wie eine Glühbirne herausschießen, was ungefähr 60% weniger Zeit kostet. Jetzt wissen Sie, dass Sie, wenn Sie 5 verschiedene Proben genommen hätten, diese möglicherweise nur zweimal oder bis zu 4 Mal gesehen hätten. Die 60% -Messung entspricht also eher einem allgemeinen Bereich von 40% bis 80%. Wenn es nur 40% wären, würden Sie sagen, dass das Problem nicht behoben werden sollte? Was ist also der Zeitpunkt der Genauigkeit, wenn Sie wirklich die Probleme finden möchten ? 500 oder 5000 Proben hätten das Problem genauer gemessen, es aber nicht genauer gefunden.
Das Zählen von Anweisungs- oder Funktionsaufrufen ist nützlich.
Angenommen, Sie wissen, dass eine Funktion 1000 Mal aufgerufen wurde. Können Sie daraus erkennen, welchen Bruchteil der Zeit es kostet? Sie müssen auch wissen, wie lange es im Durchschnitt dauert, es mit der Anzahl zu multiplizieren und durch die Gesamtzeit zu dividieren. Die durchschnittliche Aufrufzeit kann von Nanosekunden bis Sekunden variieren, sodass die Anzahl allein nicht viel aussagt. Wenn es Stapelstichproben gibt, sind die Kosten für eine Routine oder eine Anweisung nur der Bruchteil der Stichproben, auf denen sie sich befindet. Dieser Zeitanteil könnte im Prinzip insgesamt eingespart werden, wenn die Routine oder Aussage so gestaltet werden könnte, dass sie keine Zeit in Anspruch nimmt. Dies hat also den direktesten Bezug zur Leistung.
dass Samples nicht genommen werden müssen, wenn sie blockiert sind
Die Gründe für diesen Mythos sind zweierlei: 1) dass PC-Sampling bedeutungslos ist, wenn das Programm wartet, und 2) die Sorge um die Genauigkeit des Timings. Für (1) wartet das Programm jedoch möglicherweise auf etwas, nach dem es gefragt hat, z. B. Datei-E / A, die Sie kennen müssen und die Stapelbeispiele anzeigen. (Natürlich möchten Sie Samples ausschließen, während Sie auf Benutzereingaben warten.) Für (2) Wenn das Programm nur aufgrund der Konkurrenz mit anderen Prozessen wartet, geschieht dies vermutlich auf ziemlich zufällige Weise, während es ausgeführt wird. Während das Programm möglicherweise länger dauert, hat dies keine großen Auswirkungen auf die wichtige Statistik, den Prozentsatz der Zeit, in der sich Anweisungen auf dem Stapel befinden.
dass "Selbstzeit" wichtig ist
Selbstzeit ist nur dann sinnvoll, wenn Sie auf Funktionsebene und nicht auf Zeilenebene messen und Sie denken, Sie brauchen Hilfe, um zu erkennen, ob die Funktionszeit für rein lokale Berechnungen im Vergleich zu aufgerufenen Routinen verwendet wird. Wenn auf Zeilenebene zusammengefasst wird, repräsentiert eine Linie die Eigenzeit, wenn sie sich am Ende des Stapels befindet, andernfalls repräsentiert sie die Inklusivzeit. In beiden Fällen kostet es den Prozentsatz der Stapelmuster, auf denen es sich befindet, sodass es in beiden Fällen für Sie gefunden wird.
dass Proben mit hoher Frequenz entnommen werden müssen
Dies beruht auf der Idee, dass ein Leistungsproblem schnell wirken kann und dass Proben häufig sein müssen, um es zu treffen. Wenn das Problem jedoch 20% der Gesamtlaufzeit von 10 Sekunden (oder was auch immer) kostet, hat jede Probe in dieser Gesamtzeit eine 20% ige Chance, es zu treffen, unabhängig davon, ob das Problem auftritt In einem Einzelstück wie diesem
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 Proben, 4 Treffer)
oder in vielen kleinen Stücken wie diesem
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 Proben, 3 Treffer)
beträgt die durchschnittliche Anzahl der Treffer in jedem Fall etwa 1 zu 5, unabhängig davon, wie viele Proben entnommen werden, oder wie wenige. (Durchschnitt = 20 * 0,2 = 4. Standardabweichung = +/- sqrt (20 * 0,2 * 0,8) = 1,8.)
dass Sie versuchen, den Engpass zu finden,
als ob es nur einen gäbe. Betrachten Sie die folgende Ausführungszeitleiste: vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
Sie besteht aus wirklich nützlicher Arbeit, dargestellt durch .
. Es gibt Leistungsprobleme, vWxYz
die jeweils 1/2, 1/4, 1/8, 1/16, 1/32 der Zeit in Anspruch nehmen. Probenahme v
leicht zu finden. Es wird entfernt, sodass
xWzWxWYWxW.WxWYW
das Programm nur halb so lange und jetzt W
die Hälfte der Zeit benötigt und leicht zu finden ist. Es wird entfernt, so dass
xzxYx.xY
dieser Vorgang jedes Mal fortgesetzt wird, wenn das größte prozentuale Leistungsproblem entfernt wird, bis nichts mehr zu entfernen ist. Jetzt wird nur noch ausgeführt .
, was in 1/32 der vom ursprünglichen Programm verwendeten Zeit ausgeführt wird . Dies ist der Vergrößerungseffekt, wodurch das Entfernen eines Problems den Rest um Prozent vergrößert, weil der Nenner reduziert wird.
Ein weiterer entscheidender Punkt ist, dass jedes einzelne Problem gefunden werden muss - es fehlt keines der 5. Jedes Problem, das nicht gefunden und behoben wurde, verringert das endgültige Beschleunigungsverhältnis erheblich. Nur einige, aber nicht alle zu finden, ist nicht "gut genug".
HINZUGEFÜGT: Ich möchte nur einen Grund nennen, warum gprof beliebt ist - es wird unterrichtet, vermutlich weil es kostenlos und einfach zu unterrichten ist und es schon lange gibt. Eine schnelle Google-Suche findet einige akademische Institutionen, die sie lehren (oder scheinen):
berkeley bu clemson colorado duke earlham fsu indiana mit msu ncsa.illinois
** Mit Ausnahme anderer Möglichkeiten, die Ausführung von Arbeiten anzufordern, hinterlässt dies keine Spur, die erklärt, warum , z. B. durch das Posten von Nachrichten.
Da ich hier nichts gesehen habe, perf
was ein relativ neues Tool zum Profilieren des Kernels und der Benutzeranwendungen unter Linux ist, habe ich beschlossen, diese Informationen hinzuzufügen.
Zunächst einmal - dies ist ein Tutorial zum Erstellen von Linux-Profilenperf
Sie können verwenden, perf
wenn Ihr Linux-Kernel größer als 2.6.32 ist oder oprofile
wenn er älter ist. Beide Programme erfordern nicht, dass Sie Ihr Programm instrumentieren (wie gprof
erforderlich). Um das Aufrufdiagramm jedoch korrekt zu erhalten perf
, müssen Sie Ihr Programm mit erstellen -fno-omit-frame-pointer
. Zum Beispiel : g++ -fno-omit-frame-pointer -O2 main.cpp
.
Sie können eine "Live" -Analyse Ihrer Anwendung sehen mit perf top
:
sudo perf top -p `pidof a.out` -K
Oder Sie können Leistungsdaten einer laufenden Anwendung aufzeichnen und anschließend analysieren:
1) So zeichnen Sie Leistungsdaten auf:
perf record -p `pidof a.out`
oder für 10 Sekunden aufnehmen:
perf record -p `pidof a.out` sleep 10
oder mit Call Graph () aufnehmen
perf record -g -p `pidof a.out`
2) Um die aufgezeichneten Daten zu analysieren
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
Oder Sie können Leistungsdaten einer Anwendung aufzeichnen und anschließend analysieren, indem Sie die Anwendung auf diese Weise starten und auf das Beenden warten:
perf record ./a.out
Dies ist ein Beispiel für die Profilerstellung eines Testprogramms
Das Testprogramm befindet sich in der Datei main.cpp (ich werde main.cpp am Ende der Nachricht einfügen):
Ich kompiliere es so:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
Ich benutze, libmalloc_minimial.so
da es mit kompiliert wird, -fno-omit-frame-pointer
während libc malloc ohne diese Option kompiliert zu sein scheint. Dann starte ich mein Testprogramm
./my_test 100000000
Dann zeichne ich Leistungsdaten eines laufenden Prozesses auf:
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
Dann analysiere ich die Last pro Modul:
perf report --stdio -g none --sort comm, dso -i ./my_test.perf.data
# Overhead Command Shared Object
# ........ ....... ............................
#
70.06% my_test my_test
28.33% my_test libtcmalloc_minimal.so.0.1.0
1.61% my_test [kernel.kallsyms]
Dann wird die Last pro Funktion analysiert:
perf report --stdio -g none -i ./my_test.perf.data | c ++ filt
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
29.14% my_test my_test [.] f1(long)
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
9.44% my_test my_test [.] process_request(long)
1.01% my_test my_test [.] operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
0.13% my_test [kernel.kallsyms] [k] native_write_msr_safe
and so on ...
Dann werden Anrufketten analysiert:
perf report --stdio -g graph -i ./my_test.perf.data | c ++ filt
# Overhead Command Shared Object Symbol
# ........ ....... ............................ ...........................
#
29.30% my_test my_test [.] f2(long)
|
--- f2(long)
|
--29.01%-- process_request(long)
main
__libc_start_main
29.14% my_test my_test [.] f1(long)
|
--- f1(long)
|
|--15.05%-- process_request(long)
| main
| __libc_start_main
|
--13.79%-- f2(long)
process_request(long)
main
__libc_start_main
15.17% my_test libtcmalloc_minimal.so.0.1.0 [.] operator new(unsigned long)
|
--- operator new(unsigned long)
|
|--11.44%-- f1(long)
| |
| |--5.75%-- process_request(long)
| | main
| | __libc_start_main
| |
| --5.69%-- f2(long)
| process_request(long)
| main
| __libc_start_main
|
--3.01%-- process_request(long)
main
__libc_start_main
13.16% my_test libtcmalloc_minimal.so.0.1.0 [.] operator delete(void*)
|
--- operator delete(void*)
|
|--9.13%-- f1(long)
| |
| |--4.63%-- f2(long)
| | process_request(long)
| | main
| | __libc_start_main
| |
| --4.51%-- process_request(long)
| main
| __libc_start_main
|
|--3.05%-- process_request(long)
| main
| __libc_start_main
|
--0.80%-- f2(long)
process_request(long)
main
__libc_start_main
9.44% my_test my_test [.] process_request(long)
|
--- process_request(long)
|
--9.39%-- main
__libc_start_main
1.01% my_test my_test [.] operator delete(void*)@plt
|
--- operator delete(void*)@plt
0.97% my_test my_test [.] operator new(unsigned long)@plt
|
--- operator new(unsigned long)@plt
0.20% my_test my_test [.] main
0.19% my_test [kernel.kallsyms] [k] apic_timer_interrupt
0.16% my_test [kernel.kallsyms] [k] _spin_lock
and so on ...
An diesem Punkt wissen Sie also, wo Ihr Programm Zeit verbringt.
Und das ist main.cpp für den Test:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
time_t f1(time_t time_value)
{
for (int j =0; j < 10; ++j) {
++time_value;
if (j%5 == 0) {
double *p = new double;
delete p;
}
}
return time_value;
}
time_t f2(time_t time_value)
{
for (int j =0; j < 40; ++j) {
++time_value;
}
time_value=f1(time_value);
return time_value;
}
time_t process_request(time_t time_value)
{
for (int j =0; j < 10; ++j) {
int *p = new int;
delete p;
for (int m =0; m < 10; ++m) {
++time_value;
}
}
for (int i =0; i < 10; ++i) {
time_value=f1(time_value);
time_value=f2(time_value);
}
return time_value;
}
int main(int argc, char* argv2[])
{
int number_loops = argc > 1 ? atoi(argv2[1]) : 1;
time_t time_value = time(0);
printf("number loops %d\n", number_loops);
printf("time_value: %d\n", time_value );
for (int i =0; i < number_loops; ++i) {
time_value = process_request(time_value);
}
printf("time_value: %ld\n", time_value );
return 0;
}
f1
riefen an delete
. 40% (ungefähr) der Zeit process_request
riefen an delete
. Ein großer Teil des Restes wurde in ausgegeben new
. Die Messungen sind grob, aber die Hotspots sind genau bestimmt.
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
. 1) Ich denke, dass Ihre Technik nicht nützlich ist, wenn Sie Leistungsprobleme für ein Programm analysieren müssen, das auf dem Server Ihres Kunden ausgeführt wird. 2) Ich bin nicht sicher, wie Sie diese Technik anwenden, um Informationen für ein Programm mit vielen Threads zu erhalten, die unterschiedliche Anforderungen verarbeiten. Ich meine, wenn das allgemeine Bild ziemlich kompliziert ist.
the problem is outside your code
, oder? Da benötigen Sie möglicherweise einige Informationen, um Ihren Standpunkt zu unterstützen. In dieser Situation müssen Sie möglicherweise irgendwann Ihre Anwendung profilieren. Sie können Ihren Kunden nicht einfach bitten, gdb zu starten und ^ C zu drücken, um Anrufstapel zu erhalten. Das war mein Punkt. Dies ist ein Beispiel für spielwiese.fontein.de/2012/01/22/… . Ich hatte dieses Problem und die Profilerstellung hat mir sehr geholfen.
Versuchen Sie es mit OProfile . Es ist ein viel besseres Tool zum Profilieren Ihres Codes. Ich würde auch Intel VTune vorschlagen .
Mit den beiden oben genannten Tools können Sie die in einer bestimmten Codezeile verbrachte Zeit eingrenzen, Ihren Code mit Anmerkungen versehen, die Assembly anzeigen und festlegen, wie viel bestimmte Anweisungen erforderlich sind. Neben der Zeitmetrik können Sie auch bestimmte Zähler abfragen, z. B. Cache-Treffer usw.
Im Gegensatz zu gprof können Sie jeden Prozess / jede Binärdatei, die auf Ihrem System ausgeführt wird, mit einem der beiden Profile profilieren.
Zu den Leistungstools von Google gehört ein einfach zu verwendender Profiler. CPU sowie Heap-Profiler sind verfügbar.
Schauen Sie sich Sysprof an .
Ihre Distribution hat es möglicherweise bereits.
http://lttng.org/ wenn Sie einen Hochleistungs-Tracer möchten