Obwohl ich die meisten Ergebnisse intuitiv erhalten kann, fällt es mir schwer, die Ausgabe von vollständig zu verstehen perf report
Befehl speziell für den Anrufgraphen, also habe ich einen dummen Test geschrieben, um dieses Problem von mir ein für alle Mal zu lösen.
Der dumme Test
Folgendes habe ich zusammengestellt mit:
gcc -Wall -pedantic -lm perf-test.c -o perf-test
Keine aggressiven Optimierungen zur Vermeidung von Inlining und dergleichen.
#include <math.h>
#define N 10000000UL
#define USELESSNESS(n) \
do { \
unsigned long i; \
double x = 42; \
for (i = 0; i < (n); i++) x = sin(x); \
} while (0)
void baz()
{
USELESSNESS(N);
}
void bar()
{
USELESSNESS(2 * N);
baz();
}
void foo()
{
USELESSNESS(3 * N);
bar();
baz();
}
int main()
{
foo();
return 0;
}
Flache Profilierung
perf record ./perf-test
perf report
Mit diesen bekomme ich:
94,44% perf-test libm-2.19.so [.] __sin_sse2
2,09% perf-test perf-test [.] [email protected]
1,24% perf-test perf-test [.] foo
0,85% perf-test perf-test [.] baz
0,83% perf-test perf-test [.] bar
Was vernünftig klingt, da die schwere Arbeit tatsächlich von durchgeführt wird __sin_sse2
und [email protected]
ist wahrscheinlich nur ein Wrapper, während der Overhead meiner Funktionen insgesamt nur die Schleife berücksichtigt: 3*N
Iterationen für foo
, 2*N
für die anderen beiden.
Hierarchisches Profiling
perf record -g ./perf-test
perf report -G
perf report
Jetzt sind die Overhead-Spalten, die ich bekomme, zwei: Children
(die Ausgabe wird standardmäßig danach sortiert) und Self
(gleicher Overhead des Flachprofils).
Hier beginne ich zu fühlen, dass ich etwas vermisse: unabhängig davon, dass ich es benutze -G
oder nicht, ich kann die Hierarchie nicht in Bezug auf “x ruft y” oder “y wird von x angerufen” erklären, zum Beispiel:
-
ohne
-G
(“y wird von x aufgerufen”):- 94,34% 94,06% perf-test libm-2.19.so [.] __sin_sse2 - __sin_sse2 + 43,67% foo + 41,45% main + 14,88% bar - 37,73% 0,00% perf-test perf-test [.] main main __libc_start_main - 23,41% 1,35% perf-test perf-test [.] foo foo main __libc_start_main - 6,43% 0,83% perf-test perf-test [.] bar bar foo main __libc_start_main - 0,98% 0,98% perf-test perf-test [.] baz - baz + 54,71% foo + 45,29% bar
- Warum
__sin_sse2
wird von angerufenmain
(indirekt?),foo
undbar
aber nicht durchbaz
? - Warum Funktionen manchmal einen Prozentsatz und eine Hierarchie angehängt haben (z. B. die letzte Instanz von
baz
) und manchmal nicht (z. B. die letzte Instanz vonbar
)?
- Warum
-
mit
-G
(“x ruft y an”):- 94,34% 94,06% perf-test libm-2.19.so [.] __sin_sse2 + __sin_sse2 + __libc_start_main + main - 37,73% 0,00% perf-test perf-test [.] main - main + 62,05% foo + 35,73% __sin_sse2 2,23% [email protected] - 23,41% 1,35% perf-test perf-test [.] foo - foo + 64,40% __sin_sse2 + 29,18% bar + 3,98% [email protected] 2,44% baz __libc_start_main main foo
- Wie soll ich die ersten drei Einträge unter interpretieren
__sin_sse2
? main
Anrufefoo
und das ist ok, aber warum, wenn es anruft__sin_sse2
und[email protected]
(indirekt?) es ruft auch nicht anbar
undbaz
?- Warum machen
__libc_start_main
undmain
unter erscheinenfoo
? Und warumfoo
taucht zweimal auf?
- Wie soll ich die ersten drei Einträge unter interpretieren
Vermutlich gibt es zwei Ebenen dieser Hierarchie, in denen die zweite tatsächlich die Semantik “x ruft y”https://stackoverflow.com/”y wird von x aufgerufen” darstellt, aber ich bin müde zu raten, also ich’ Ich frage hier. Und die Dokumentation scheint nicht zu helfen.
Entschuldigung für den langen Beitrag, aber ich hoffe, dass all dieser Kontext auch jemand anderem helfen oder als Referenz dienen kann.
Ich bin kein Experte für
perf
, aber ich weiß, dass es standardmäßig ~ 1000 Mal pro Sekunde auf den Stapel schaut, um seine Daten zu sammeln. Daher wird eine feinkörnige Analyse, wie Sie es versuchen, wahrscheinlich fehlschlagen. So ist es zB möglich, dass keines der Samples wann aufgetreten istsin_sse2
wurde angerufenbaz
. Erwägen Sie die Verwendunggprof
das in Stubs kompiliert wird, um jeden Anruf abzufangen und zurückzugeben (obwohl es andere Probleme hat).– Gen
2. Januar 2015 um 16:34 Uhr
Ja, ich weiß, aber es ist schnell und ließ mich alle möglichen verrückten Ereignisse wie Cache-Fehlschläge und falsche Verzweigungsvorhersage pro Symbol aufzeichnen, während AFAIK
gprof
kann es nicht. ich benutzteN
ziemlich groß, genau um das zu vermeiden, was Sie erwähnt haben; Jedenfalls habe ich ohne Glück versucht, es noch weiter zu erhöhen, ich weiß es nicht, aber ich denke, dass es ziemlich unwahrscheinlich ist, dass selbst in einer 100M-Iterationsschleife keine Samples gesammelt werden.– cYrus
2. Januar 2015 um 17:35 Uhr