Jakie inne programy robią to samo co gprof?
Jakie inne programy robią to samo co gprof?
Odpowiedzi:
Valgrind ma profiler zliczania instrukcji z bardzo ładnym wizualizatorem o nazwie KCacheGrind . Jak radzi Mike Dunlavey, Valgrind liczy ułamek instrukcji, dla których procedura jest aktywna na stosie, chociaż przykro mi to mówić, wydaje się być zdezorientowana w obecności wzajemnej rekursji. Ale wizualizator jest bardzo ładny i wyprzedza go o lata świetlne gprof
.
gprof (przeczytaj artykuł) istnieje ze względów historycznych. Jeśli uważasz, że pomoże Ci to znaleźć problemy z wydajnością, nigdy nie był reklamowany jako taki. Oto, co mówi artykuł:
Profil można wykorzystać do porównania i oceny kosztów różnych wdrożeń.
Nie mówi, że można go użyć do zidentyfikowania różnych wdrożeń do oceny, chociaż sugeruje , że w szczególnych okolicznościach może:
zwłaszcza jeśli okaże się, że małe fragmenty programu dominują w czasie wykonywania.
A co z problemami, które nie są tak zlokalizowane? Czy to nie ma znaczenia? Nie stawiaj na gprof oczekiwań , które nigdy nie zostały zgłoszone. To tylko narzędzie pomiarowe i tylko operacje związane z procesorem.
Spróbuj tego zamiast tego.
Oto przykład 44-krotnego przyspieszenia.
Oto przyspieszenie 730x.
Oto 8-minutowa demonstracja wideo.
Oto wyjaśnienie statystyk.
Oto odpowiedź na krytykę.
Jest prosta obserwacja dotycząca programów. W danej realizacji każda instrukcja odpowiada za jakiś ułamek całkowitego czasu (zwłaszczacall
instrukcje), w tym sensie, że gdyby jej nie było, czas nie zostałby wykorzystany. W tym czasie instrukcja znajduje się na stosie **. Kiedy to zrozumiesz, zobaczysz, że -
gprof uosabia pewne mity dotyczące wydajności, takie jak:
że próbkowanie licznika programu jest przydatne.
Jest to przydatne tylko wtedy, gdy masz niepotrzebne wąskie gardło punktu aktywnego, takie jak bąbelkowy rodzaj dużej tablicy wartości skalarnych. Gdy na przykład zmienisz to na sortowanie za pomocą porównywania ciągów, nadal jest to wąskie gardło, ale próbkowanie licznika programowego tego nie zauważy, ponieważ teraz punkt aktywny jest w porównywaniu ciągów. Z drugiej strony, gdyby miał próbkować rozszerzony plik licznik programu (stos wywołań), punkt, w którym wywoływana jest funkcja porównywania łańcuchów, pętla sortowania, jest wyraźnie wyświetlany. W rzeczywistości gprof był próbą zaradzenia ograniczeniom próbkowania tylko dla komputerów PC.
że funkcje czasowe są ważniejsze niż przechwytywanie czasochłonnych wierszy kodu.
Powodem tego mitu jest to, że gprof nie był w stanie przechwycić próbek stosu, więc zamiast funkcje, liczy ich wywołania i próbuje przechwycić wykres wywołań. Jednak po zidentyfikowaniu kosztownej funkcji nadal musisz szukać w niej wierszy odpowiedzialnych za czas. Gdyby były próbki w stosie, których nie musiałbyś szukać, te linie byłyby na próbkach. (Typowa funkcja może mieć od 100 do 1000 instrukcji. Wywołanie funkcji to 1 instrukcja, więc coś, co lokalizuje kosztowne wywołania, jest o 2-3 rzędy wielkości dokładniejsze).
że wykres wywołań jest ważny.
To, co musisz wiedzieć o programie, to nie to, gdzie spędza swój czas, ale dlaczego. Kiedy spędza czas w funkcji, każda linia kodu na stosie daje jedno ogniwo w łańcuchu rozumowania, dlaczego tam się znajduje. Jeśli widzisz tylko część stosu, możesz zobaczyć tylko część powodu, więc nie możesz stwierdzić na pewno, czy ten czas jest rzeczywiście potrzebny. Co mówi wykres połączeń? Każdy łuk mówi ci, że jakaś funkcja A była w trakcie wywoływania jakiejś funkcji B przez pewien ułamek czasu. Nawet jeśli A ma tylko jedną taką linię kodu wywołującą B, ta linia podaje tylko niewielką część przyczyny. Jeśli masz szczęście, może ta linia ma kiepski powód. Zwykle musisz zobaczyć wiele jednoczesnych linii, aby znaleźć zły powód, jeśli taki istnieje. Jeśli A dzwoni do B w więcej niż jednym miejscu, powie Ci jeszcze mniej.
że rekurencja jest trudnym i mylącym problemem. Dzieje się
tak tylko dlatego, że gprof i inni profilery dostrzegają potrzebę wygenerowania wykresu wywołań, a następnie przypisania czasów do węzłów. Jeśli ktoś ma próbki stosu, koszt czasu każdego wiersza kodu, który pojawia się na próbkach, jest bardzo prostą liczbą - ułamkiem próbek, na których się znajduje. Jeśli występuje rekursja, to dana linia może pojawić się więcej niż raz na próbce.
Bez znaczenia. Załóżmy, że próbki są pobierane co N ms, a linia pojawia się na F% z nich (pojedynczo lub nie). Jeśli można sprawić, że ta linia nie zajmie czasu (na przykład przez usunięcie jej lub rozgałęzienie wokół niej), wtedy te próbki znikną , a czas zostanie skrócony o F%.
że dokładność pomiaru czasu (a tym samym duża liczba próbek) jest ważna.
Pomyśl o tym przez chwilę. Jeśli wiersz kodu zawiera 3 próbki na pięć, to gdybyś mógł go wystrzelić jak żarówkę, jest to około 60% mniej czasu, który zostałby wykorzystany. Teraz wiesz, że gdybyś pobrał różne 5 próbek, mógłbyś zobaczyć to tylko 2 razy lub aż 4. Tak więc pomiar 60% bardziej przypomina ogólny zakres od 40% do 80%. Gdyby to było tylko 40%, czy powiedziałbyś, że problemu nie warto naprawiać? Jaki jest więc punkt dokładności czasowej, kiedy naprawdę chcesz znaleźć problemy ? 500 lub 5000 próbek zmierzyłoby problem z większą precyzją, ale nie wykryłoby go dokładniej.
że liczenie wywołań instrukcji lub funkcji jest przydatne.
Załóżmy, że wiesz, że funkcja została wywołana 1000 razy. Czy możesz z tego stwierdzić, ile czasu to kosztuje? Musisz także wiedzieć, jak długo trwa średnio, pomnożyć to przez liczbę i podzielić przez całkowity czas. Średni czas wywołania może wahać się od nanosekund do sekund, więc sam licznik niewiele mówi. Jeśli są próbki w stosie, koszt procedury lub dowolnego oświadczenia to tylko ułamek próbek, w których się znajdują. Ten ułamek czasu w zasadzie można by ogólnie zaoszczędzić, gdyby rutyna lub stwierdzenie nie zajmowało czasu, więc to właśnie ma najbardziej bezpośredni związek z wynikami.
że próbki nie muszą być pobierane, gdy są zablokowane
. Przyczyny tego mitu są dwojakie: 1) próbkowanie z komputera jest bez znaczenia, gdy program czeka, oraz 2) zainteresowanie dokładnością synchronizacji. Jednak w przypadku (1) program może bardzo dobrze czekać na coś, o co prosił, na przykład we / wy pliku, o czym musisz wiedzieć i które próbki stosu ujawniają. (Oczywiście chcesz wykluczyć próbki podczas oczekiwania na dane wejściowe użytkownika). W przypadku (2), jeśli program czeka po prostu z powodu konkurencji z innymi procesami, prawdopodobnie dzieje się to w dość przypadkowy sposób podczas jego działania. Więc chociaż program może zająć więcej czasu, nie będzie to miało dużego wpływu na statystyki, które mają znaczenie, procent czasu, przez jaki instrukcje znajdują się na stosie.
że "czas własny" ma znaczenie
Czas jaźni ma sens tylko wtedy, gdy mierzysz na poziomie funkcji, a nie na poziomie liniowym i myślisz, że potrzebujesz pomocy w rozeznaniu, czy czas funkcji przechodzi do czysto lokalnych obliczeń, czy też w wywołanych procedurach. W przypadku podsumowania na poziomie liniowym linia reprezentuje czas własny, jeśli znajduje się na końcu stosu, w przeciwnym razie reprezentuje czas włącznie. Tak czy inaczej, to, co kosztuje, to procent próbek stosu, na którym się znajduje, więc w każdym przypadku lokalizuje go dla Ciebie.
że próbki muszą być pobierane z dużą częstotliwością
Wynika to z pomysłu, że problem z wydajnością może występować szybko i że próbki muszą być częste, aby go uderzyć. Ale jeśli problem kosztuje, powiedzmy 20% z całkowitego czasu działania wynoszącego 10 sekund (lub cokolwiek), to każda próbka w tym łącznym czasie będzie miała 20% szansy na trafienie, bez względu na to, czy problem wystąpi w jednym kawałku, takim jak ten
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 próbek, 4 trafienia)
lub w wielu małych kawałkach takich jak ten
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 próbek, 3 trafienia)
Tak czy inaczej, liczba trafień wyniesie średnio około 1 do 5, niezależnie od tego, ile próbek zostanie pobranych, lub jak mało. (Średnia = 20 * 0,2 = 4. Odchylenie standardowe = +/- sqrt (20 * 0,2 * 0,8) = 1,8.)
które próbują odnaleźć się wąskim gardłem
, jakby było tylko jedno. Rozważ następujący harmonogram wykonania: vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
Składa się z naprawdę użytecznej pracy, reprezentowanej przez .
. Występują problemy vWxYz
z wydajnością zajmujące odpowiednio 1/2, 1/4, 1/8, 1/16, 1/32 czasu. Pobieranie próbek jest v
łatwe. Jest usuwany, pozostawiając
xWzWxWYWxW.WxWYW
Teraz program trwa o połowę krócej, teraz W
zajmuje połowę czasu i można go łatwo znaleźć. Jest usuwany, pozostawiając
xzxYx.xY
Ten proces jest kontynuowany, za każdym razem usuwając procentowo największy problem z wydajnością, aż nie można znaleźć niczego do usunięcia. Teraz jedyną wykonywaną rzeczą jest .
wykonanie przez 1/32 czasu używanego przez oryginalny program. To jest efekt powiększenia, przez co usunięcie problemu powoduje zwiększenie pozostałej części o procent, ponieważ zmniejsza się mianownik.
Kolejną istotną kwestią jest to, że każdy problem musi zostać znaleziony - nie pomijając żadnego z 5. Każdy problem, który nie został znaleziony i naprawiony, poważnie zmniejsza ostateczny współczynnik przyspieszenia. Samo znalezienie niektórych, ale nie wszystkich, nie jest „wystarczająco dobre”.
DODANE: Chciałbym tylko wskazać jeden powód, dla którego gprof jest popularny - jest nauczany, prawdopodobnie dlatego, że jest darmowy, łatwy do nauczenia i istnieje od dawna. Szybkie wyszukiwanie w Google pozwala zlokalizować niektóre instytucje akademickie, które tego nauczają (lub wydają się):
berkeley bu clemson colorado duke Earlham fsu indiana mit msu ncsa.illinois ncsu nyu ou princeton psu stanford ucsd umd umich utah utexas utk wustl
** Z wyjątkiem innych sposobów zgłaszania prośby o wykonanie pracy, które nie pozostawiają śladu wyjaśniającego dlaczego , na przykład wysyłanie wiadomości.
Ponieważ nic tu nie widziałem perf
co jest stosunkowo nowym narzędziem do profilowania jądra i aplikacji użytkownika w systemie Linux, postanowiłem dodać te informacje.
Po pierwsze - to jest tutorial o profilowaniu Linuksa zperf
Możesz użyć, perf
jeśli twoje jądro Linuksa jest większe niż 2.6.32 lub oprofile
jeśli jest starsze. Oba programy nie wymagają od ciebie instrumentowania twojego programu (jak gprof
wymagania). Jednak aby poprawnie uzyskać wykres wywołań w programie perf
, musisz zbudować swój program -fno-omit-frame-pointer
. Na przykład:g++ -fno-omit-frame-pointer -O2 main.cpp
.
Możesz zobaczyć analizę swojej aplikacji „na żywo” za pomocą perf top
:
sudo perf top -p `pidof a.out` -K
Możesz też zarejestrować dane o wydajności uruchomionej aplikacji i przeanalizować je później:
1) Aby zarejestrować dane dotyczące wykonania:
perf record -p `pidof a.out`
lub nagrywać przez 10 sekund:
perf record -p `pidof a.out` sleep 10
lub nagrywać z wykresem wywołań ()
perf record -g -p `pidof a.out`
2) Analiza zarejestrowanych danych
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
Możesz też zarejestrować dane wydajności aplikacji i przeanalizować je po prostu uruchamiając aplikację w ten sposób i czekając na jej zamknięcie:
perf record ./a.out
To jest przykład profilowania programu testowego
Program testowy znajduje się w pliku main.cpp (umieszczę main.cpp na dole wiadomości):
Kompiluję to w ten sposób:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
Używam, libmalloc_minimial.so
ponieważ jest kompilowany z, -fno-omit-frame-pointer
podczas gdy libc malloc wydaje się być kompilowany bez tej opcji. Następnie uruchamiam program testowy
./my_test 100000000
Następnie rejestruję dane wydajności uruchomionego procesu:
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
Następnie analizuję obciążenie na moduł:
raport perf --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]
Następnie analizowane jest obciążenie na funkcję:
raport perf --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 ...
Następnie analizowane są łańcuchy połączeń:
raport perf --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 ...
W tym momencie wiesz, gdzie Twój program spędza czas.
A to jest main.cpp do testu:
#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
dzwoniło delete
. 40% (mniej więcej) czasu process_request
dzwoniło delete
. Znaczną część pozostałej części wydano w new
. Pomiary są zgrubne, ale punkty aktywne są precyzyjne.
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
. 1) Myślę, że twoja technika nie jest przydatna, gdy musisz analizować problemy z wydajnością programu działającego na serwerze klienta. 2) Nie jestem pewien, jak zastosować tę technikę do uzyskania informacji dla programu mającego wiele wątków, które obsługują różne żądania. Mam na myśli, kiedy ogólny obraz jest dość skomplikowany.
the problem is outside your code
, prawda? Ponieważ możesz potrzebować pewnych informacji, aby poprzeć swój punkt widzenia. W takiej sytuacji w pewnym momencie może być konieczne profilowanie aplikacji. Nie możesz po prostu poprosić klienta o rozpoczęcie gdb i naciśnięcie ^ C i pobranie stosów połączeń. To był mój punkt widzenia. To jest przykład spielwiese.fontein.de/2012/01/22/… . Miałem ten problem i profilowanie bardzo mi pomogło.
Wypróbuj OProfile . Jest to znacznie lepsze narzędzie do profilowania Twojego kodu. Sugerowałbym również Intel VTune .
Dwa powyższe narzędzia mogą zawęzić czas spędzony w określonej linii kodu, dodać adnotacje do kodu, pokazać asemblację i ile zajmuje konkretna instrukcja. Oprócz metryki czasu można również zapytać o określone liczniki, np. Trafienia w pamięci podręcznej itp.
W przeciwieństwie do gprof, możesz profilować dowolny proces / plik binarny działający w systemie, używając jednego z dwóch.
Narzędzia wydajności Google obejmują prosty w użyciu profiler. Dostępny jest procesor oraz profiler sterty.
Spójrz na Sysprof .
Twoja dystrybucja może już to mieć.
http://lttng.org/, jeśli chcesz mieć znacznik o wysokiej wydajności