Program apa lagi yang melakukan hal yang sama dengan gprof?
Program apa lagi yang melakukan hal yang sama dengan gprof?
Jawaban:
Valgrind memiliki profiler penghitungan instruksi dengan visualizer yang sangat bagus bernama KCacheGrind . Seperti yang direkomendasikan oleh Mike Dunlavey, Valgrind menghitung sebagian kecil dari instruksi yang prosedurnya tinggal di stack, meskipun saya menyesal mengatakan itu tampaknya menjadi bingung di hadapan rekursi timbal balik. Tetapi visualisator sangat bagus dan tahun cahaya di depan gprof
.
gprof (baca koran) ada karena alasan historis. Jika Anda pikir itu akan membantu Anda menemukan masalah kinerja, itu tidak pernah diiklankan seperti itu. Inilah yang dikatakan oleh makalah ini:
Profil dapat digunakan untuk membandingkan dan menilai biaya berbagai implementasi.
Itu tidak mengatakan itu dapat digunakan untuk mengidentifikasi berbagai implementasi untuk dinilai, meskipun itu menyiratkan bahwa itu bisa, dalam keadaan khusus:
terutama jika sebagian kecil dari program ditemukan mendominasi waktu pelaksanaannya.
Bagaimana dengan masalah yang tidak begitu terlokalisir? Apakah itu tidak penting? Jangan menaruh harapan pada gprof yang tidak pernah diklaim untuk itu. Ini hanya alat pengukuran, dan hanya operasi yang terikat CPU.
Coba ini sebagai gantinya.
Berikut adalah contoh speedup 44x.
Ini speedup 730x.
Ini demonstrasi video 8 menit.
Berikut penjelasan statistiknya.
Inilah jawaban untuk kritik.
Ada pengamatan sederhana tentang program. Dalam eksekusi yang diberikan, setiap instruksi bertanggung jawab atas sebagian kecil dari keseluruhan waktu (terutama call
instruksi), dalam arti bahwa jika tidak ada di sana, waktu tidak akan dihabiskan. Selama waktu itu, instruksi ada di tumpukan **. Ketika itu dipahami, Anda dapat melihat bahwa -
gprof mewujudkan mitos tertentu tentang kinerja, seperti:
bahwa program counter sampling berguna.
Ini hanya berguna jika Anda memiliki hambatan hotspot yang tidak perlu seperti semacam gelembung dari sejumlah besar nilai skalar. Segera setelah Anda, misalnya, mengubahnya menjadi semacam menggunakan perbandingan-string, itu masih menjadi hambatan, tetapi program counter sampling tidak akan melihatnya karena sekarang hotspot berada dalam perbandingan-string. Di sisi lain jika itu untuk sampel penghitung program yang diperluas (tumpukan panggilan), titik di mana string-perbandingan disebut, loop pengurutan, ditampilkan dengan jelas. Bahkan, gprof adalah upaya untuk memperbaiki keterbatasan pengambilan sampel hanya pc.
bahwa fungsi pengaturan waktu lebih penting daripada menangkap garis kode yang menghabiskan waktu.
Alasan mitos itu adalah bahwa gprof tidak dapat menangkap sampel tumpukan, jadi alih-alih itu berfungsi, menghitung permintaan mereka, dan mencoba menangkap grafik panggilan. Namun, begitu fungsi yang mahal diidentifikasi, Anda masih perlu mencari di dalamnya untuk garis yang bertanggung jawab atas waktu. Jika ada tumpukan sampel yang tidak perlu Anda lihat, garis-garis itu akan ada di sampel. (Fungsi khas mungkin memiliki 100 - 1000 instruksi. Panggilan fungsi adalah 1 instruksi, jadi sesuatu yang menempatkan panggilan mahal adalah 2-3 urutan lebih tepat.)
bahwa grafik panggilan itu penting.
Apa yang perlu Anda ketahui tentang suatu program bukanlah di mana ia menghabiskan waktunya, tetapi mengapa. Ketika menghabiskan waktu dalam suatu fungsi, setiap baris kode pada stack memberikan satu tautan dalam rantai alasan mengapa kode itu ada. Jika Anda hanya dapat melihat sebagian dari tumpukan, Anda hanya dapat melihat sebagian alasannya, jadi Anda tidak dapat memastikan apakah waktu itu benar-benar diperlukan. Apa yang disampaikan grafik panggilan kepada Anda? Setiap busur memberitahu Anda bahwa beberapa fungsi A sedang dalam proses memanggil beberapa fungsi B untuk sebagian kecil dari waktu. Bahkan jika A hanya memiliki satu baris kode yang memanggil B, baris itu hanya memberikan sebagian kecil alasannya. Jika Anda cukup beruntung, mungkin kalimat itu memiliki alasan yang buruk. Biasanya, Anda perlu melihat beberapa garis secara bersamaan untuk menemukan alasan yang buruk jika ada. Jika A memanggil B di lebih dari satu tempat, maka itu memberitahu Anda lebih sedikit lagi.
rekursi itu adalah masalah membingungkan yang rumit.
Itu hanya karena gprof dan profiler lain merasakan kebutuhan untuk menghasilkan grafik panggilan dan kemudian atribut kali ke node. Jika seseorang memiliki sampel tumpukan, biaya waktu dari setiap baris kode yang muncul pada sampel adalah angka yang sangat sederhana - fraksi sampel itu berada. Jika ada rekursi, maka baris yang diberikan dapat muncul lebih dari satu kali pada sampel.
Tidak penting. Misalkan sampel diambil setiap N ms, dan garis muncul pada F% dari mereka (secara tunggal atau tidak). Jika garis itu dapat dibuat untuk tidak mengambil waktu (seperti dengan menghapusnya atau bercabang di sekitarnya), maka sampel tersebut akan menghilang , dan waktu akan berkurang sebesar F%.
bahwa ketepatan pengukuran waktu (dan karenanya sejumlah besar sampel) penting.
Pikirkan sebentar. Jika satu baris kode ada pada 3 sampel dari lima, maka jika Anda bisa menembaknya seperti bola lampu, kira-kira 60% lebih sedikit waktu yang akan digunakan. Sekarang, Anda tahu bahwa jika Anda telah mengambil 5 sampel berbeda, Anda mungkin hanya melihatnya 2 kali, atau sebanyak 4. Sehingga pengukuran 60% lebih seperti rentang umum dari 40% hingga 80%. Jika hanya 40%, apakah Anda akan mengatakan bahwa masalahnya tidak perlu diperbaiki? Jadi apa gunanya ketepatan waktu, padahal yang Anda inginkan adalah menemukan masalahnya ? 500 atau 5000 sampel akan mengukur masalah dengan presisi yang lebih besar, tetapi tidak akan menemukannya dengan lebih akurat.
bahwa penghitungan pemanggilan pernyataan atau fungsi berguna.
Misalkan Anda tahu suatu fungsi telah dipanggil 1000 kali. Bisakah Anda tahu dari itu berapa lama waktu yang dibutuhkan? Anda juga perlu tahu berapa lama untuk menjalankan, rata-rata, kalikan dengan jumlah, dan bagi dengan total waktu. Waktu doa rata-rata dapat bervariasi dari nanodetik hingga detik, sehingga hitungannya saja tidak banyak memberi tahu. Jika ada tumpukan sampel, biaya rutin atau pernyataan apa pun hanyalah sebagian kecil dari sampel itu on. Sepersekian waktu itulah yang pada prinsipnya dapat diselamatkan secara keseluruhan jika rutinitas atau pernyataan dapat dibuat tidak memakan waktu, sehingga itulah yang memiliki hubungan paling langsung dengan kinerja.
bahwa sampel tidak perlu diambil ketika diblokir
Alasan untuk mitos ini ada dua: 1) bahwa pengambilan sampel PC tidak ada artinya ketika program sedang menunggu, dan 2) keasyikan dengan ketepatan waktu. Namun, untuk (1) program sangat mungkin menunggu sesuatu yang diminta, seperti file I / O, yang perlu Anda ketahui , dan contoh tumpukan yang diungkap. (Jelas Anda ingin mengecualikan sampel sambil menunggu input pengguna.) Untuk (2) jika program menunggu hanya karena persaingan dengan proses lain, itu mungkin terjadi secara acak ketika berjalan. Jadi, sementara program mungkin memakan waktu lebih lama, itu tidak akan berpengaruh besar pada statistik yang penting, persentase waktu pernyataan itu ada di tumpukan.
bahwa "waktu sendiri" penting
Waktu sendiri hanya masuk akal jika Anda mengukur pada tingkat fungsi, bukan tingkat garis, dan Anda pikir Anda perlu bantuan dalam membedakan jika waktu fungsi masuk ke dalam komputasi murni lokal versus dalam rutinitas yang disebut. Jika meringkas pada level garis, garis mewakili waktu sendiri jika berada di ujung tumpukan, jika tidak maka itu mewakili waktu inklusif. Either way, berapa biayanya adalah persentase sampel tumpukan itu, sehingga menempatkan untuk Anda dalam kedua kasus.
bahwa sampel harus diambil pada frekuensi tinggi.
Ini berasal dari gagasan bahwa masalah kinerja mungkin bertindak cepat, dan bahwa sampel harus sering dilakukan untuk memukulnya. Tetapi, jika masalahnya adalah biaya, 20%, katakanlah, dari total waktu berjalan 10 detik (atau apa pun), maka masing-masing sampel dalam waktu total itu akan memiliki peluang 20% untuk memukulnya, tidak masalah jika masalah terjadi dalam satu bagian seperti ini
.....XXXXXXXX...........................
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 sampel, 4 hit)
atau dalam banyak potongan kecil seperti ini
X...X...X.X..X.........X.....X....X.....
.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^.^
(20 sampel, 3 hit)
Bagaimanapun, jumlah hit akan rata-rata sekitar 1 dalam 5, tidak peduli berapa banyak sampel yang diambil, atau seberapa sedikit. (Rata-rata = 20 * 0,2 = 4. Standar deviasi = +/- sqrt (20 * 0,2 * 0,8) = 1,8.)
bahwa Anda sedang mencoba untuk menemukan yang bottleneck
seakan hanya ada satu. Pertimbangkan timeline pelaksanaan berikut: vxvWvzvWvxvWvYvWvxvWv.vWvxvWvYvW
Ini terdiri dari pekerjaan yang sangat berguna, diwakili oleh .
. Ada masalah kinerja yang vWxYz
mengambil masing-masing 1/2, 1/4, 1/8, 1/16, 1/32. Sampling ditemukan v
dengan mudah. Itu dihapus, meninggalkan
xWzWxWYWxW.WxWYW
Sekarang program membutuhkan waktu setengah untuk berjalan, dan sekarang W
mengambil setengah waktu, dan ditemukan dengan mudah. Itu dihapus, meninggalkan
xzxYx.xY
proses ini berlanjut, setiap kali menghapus yang terbesar, berdasarkan persentase, masalah kinerja, sampai tidak ada yang dihapus dapat ditemukan. Sekarang satu-satunya yang dieksekusi adalah .
, yang dijalankan pada 1/32 dari waktu yang digunakan oleh program asli. Ini adalah efek pembesaran, Dengan menghilangkan masalah apa pun membuat sisanya lebih besar, sebesar persen, karena penyebut berkurang.
Poin penting lainnya adalah bahwa setiap masalah harus ditemukan - tidak ada yang hilang. 5. Masalah apa pun yang tidak ditemukan dan diperbaiki sangat mengurangi rasio percepatan akhir. Hanya menemukan beberapa, tetapi tidak semua, tidak "cukup baik".
TAMBAH: Saya hanya ingin menunjukkan satu alasan mengapa gprof populer - itu diajarkan, mungkin karena gratis, mudah untuk diajar, dan sudah ada sejak lama. Pencarian Google cepat menemukan beberapa institusi akademik yang mengajarkannya (atau tampaknya):
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
** Dengan pengecualian cara lain untuk meminta pekerjaan dilakukan, itu tidak meninggalkan jejak yang mengatakan mengapa , seperti dengan posting pesan.
Karena saya tidak melihat apa pun di sini perf
yang merupakan alat yang relatif baru untuk membuat profil aplikasi kernel dan pengguna di Linux, saya memutuskan untuk menambahkan informasi ini.
Pertama-tama - ini adalah tutorial tentang profil Linuxperf
Anda dapat menggunakan perf
jika Kernel Linux Anda lebih besar dari 2.6.32 atau oprofile
jika lebih tua. Kedua program tidak memerlukan dari Anda untuk instrumen program Anda (seperti gprof
membutuhkan). Namun untuk mendapatkan grafik panggilan dengan benar, perf
Anda harus membuat program -fno-omit-frame-pointer
. Sebagai contoh: g++ -fno-omit-frame-pointer -O2 main.cpp
.
Anda dapat melihat analisis "langsung" dari aplikasi Anda dengan perf top
:
sudo perf top -p `pidof a.out` -K
Atau Anda dapat merekam data kinerja aplikasi yang berjalan dan menganalisisnya setelah itu:
1) Untuk merekam data kinerja:
perf record -p `pidof a.out`
atau untuk merekam selama 10 detik:
perf record -p `pidof a.out` sleep 10
atau untuk merekam dengan grafik panggilan ()
perf record -g -p `pidof a.out`
2) Untuk menganalisis data yang direkam
perf report --stdio
perf report --stdio --sort=dso -g none
perf report --stdio -g none
perf report --stdio -g
Atau Anda dapat merekam data kinerja suatu aplikasi dan menganalisisnya setelah itu hanya dengan meluncurkan aplikasi dengan cara ini dan menunggunya untuk keluar:
perf record ./a.out
Ini adalah contoh profiling program uji
Program pengujian ada di file main.cpp (saya akan meletakkan main.cpp di bagian bawah pesan):
Saya kompilasi dengan cara ini:
g++ -m64 -fno-omit-frame-pointer -g main.cpp -L. -ltcmalloc_minimal -o my_test
Saya menggunakan libmalloc_minimial.so
karena ini dikompilasi dengan -fno-omit-frame-pointer
sementara libc malloc tampaknya dikompilasi tanpa opsi ini. Lalu saya menjalankan program pengujian saya
./my_test 100000000
Lalu saya mencatat data kinerja dari proses yang berjalan:
perf record -g -p `pidof my_test` -o ./my_test.perf.data sleep 30
Lalu saya menganalisis beban per modul:
laporan 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]
Kemudian beban per fungsi dianalisis:
laporan 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 ...
Kemudian rantai panggilan dianalisis:
laporan 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 ...
Jadi pada titik ini Anda tahu di mana program Anda menghabiskan waktu.
Dan ini main.cpp untuk pengujian:
#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
itu menelepon delete
. 40% (kurang-lebih) dari waktu process_request
itu menelepon delete
. Sebagian dari sisanya dihabiskan di new
. Pengukurannya kasar, tetapi hotspotnya ditunjukkan dengan tepat.
As in my answer, you run it under a debugger and hit ^C at a random time and capture the stack trace
. 1) Saya pikir teknik Anda tidak berguna ketika Anda perlu menganalisis masalah kinerja untuk program yang berjalan di server pelanggan Anda. 2) Saya tidak yakin bagaimana Anda menerapkan teknik ini untuk mendapatkan informasi untuk program yang memiliki banyak utas yang menangani permintaan yang berbeda. Maksud saya ketika gambaran umum cukup rumit.
the problem is outside your code
, bukan? Karena Anda mungkin memerlukan beberapa informasi untuk mendukung maksud Anda. Dalam situasi ini Anda mungkin perlu membuat profil aplikasi Anda. Anda tidak bisa hanya meminta pelanggan Anda untuk memulai gdb dan tekan ^ C dan mendapatkan tumpukan panggilan. Inilah poin saya. Ini adalah contoh spielwiese.fontein.de/2012/01/22/… . Saya punya masalah ini dan membuat profil banyak membantu.
Coba OProfile . Ini adalah alat yang jauh lebih baik untuk membuat profil kode Anda. Saya juga menyarankan Intel VTune .
Dua alat di atas dapat mempersempit waktu yang dihabiskan dalam baris kode tertentu, menjelaskan kode Anda, menunjukkan perakitan dan berapa banyak instruksi tertentu yang diperlukan. Selain metrik waktu, Anda juga dapat meminta penghitung tertentu, yaitu hit cache, dll.
Tidak seperti gprof, Anda dapat membuat profil setiap proses / biner yang berjalan di sistem Anda menggunakan salah satu dari keduanya.
Alat kinerja Google termasuk profiler yang mudah digunakan. CPU dan juga profiler tumpukan tersedia.
Lihatlah Sysprof .
Distribusi Anda mungkin sudah memilikinya.
http://lttng.org/ jika Anda ingin pelacak kinerja tinggi