Bagaimana saya bisa profil kode C ++ berjalan di Linux?


1816

Saya memiliki aplikasi C ++, berjalan di Linux, yang sedang saya optimalkan. Bagaimana saya bisa menentukan area mana dari kode saya yang berjalan lambat?


27
Jika Anda akan memberikan lebih banyak data tentang tumpukan pengembangan Anda, Anda mungkin mendapatkan jawaban yang lebih baik. Ada profiler dari Intel dan Sun tetapi Anda harus menggunakan kompiler mereka. Apakah itu pilihan?
Nazgob

2
Sudah dijawab pada tautan berikut: stackoverflow.com/questions/2497211/...
Kapil Gupta

4
Sebagian besar jawabannya adalah codeprofiler. Namun, inversi prioritas, alias cache, pertentangan sumber daya, dll. Semuanya dapat menjadi faktor dalam mengoptimalkan dan kinerja. Saya pikir orang membaca informasi ke dalam kode lambat saya . FAQ sedang mereferensikan utas ini.
Suara tak bersuara


3
Saya biasa menggunakan pstack secara acak, sebagian besar waktu akan mencetak tumpukan paling khas di mana program sebagian besar waktu, maka menunjuk ke bottleneck.
Jose Manuel Gomez Alvarez

Jawaban:


1406

Jika tujuan Anda adalah menggunakan profiler, gunakan salah satu yang disarankan.

Namun, jika Anda sedang terburu-buru dan Anda dapat secara manual menginterupsi program Anda di bawah debugger ketika sedang lambat, ada cara sederhana untuk menemukan masalah kinerja.

Hentikan beberapa kali, dan setiap kali lihat tumpukan panggilan. Jika ada beberapa kode yang membuang-buang persentase waktu, 20% atau 50% atau apa pun, itu adalah probabilitas bahwa Anda akan menangkapnya dalam tindakan pada setiap sampel. Jadi, kira-kira persentase sampel yang Anda akan melihatnya. Tidak ada dugaan yang berpendidikan. Jika Anda memiliki dugaan tentang apa masalahnya, ini akan membuktikan atau membantahnya.

Anda mungkin memiliki beberapa masalah kinerja berbagai ukuran. Jika Anda membersihkan salah satu dari mereka, yang tersisa akan mengambil persentase lebih besar, dan lebih mudah dikenali, pada lintasan selanjutnya. Efek pembesaran ini , ketika diperparah dengan berbagai masalah, dapat menyebabkan faktor percepatan yang sangat besar.

Peringatan : Pemrogram cenderung skeptis terhadap teknik ini kecuali mereka telah menggunakannya sendiri. Mereka akan mengatakan bahwa profiler memberi Anda informasi ini, tetapi itu hanya benar jika mereka mencicipi seluruh tumpukan panggilan, dan kemudian membiarkan Anda memeriksa satu set sampel acak. (Ringkasan adalah tempat wawasan hilang.) Grafik panggilan tidak memberi Anda informasi yang sama, karena

  1. Mereka tidak merangkum pada tingkat instruksi, dan
  2. Mereka memberikan ringkasan yang membingungkan di hadapan rekursi.

Mereka juga akan mengatakan itu hanya bekerja pada program mainan, ketika sebenarnya itu bekerja pada program apa pun, dan tampaknya bekerja lebih baik pada program yang lebih besar, karena mereka cenderung memiliki lebih banyak masalah untuk ditemukan. Mereka akan mengatakan itu kadang-kadang menemukan hal-hal yang tidak masalah, tetapi itu hanya benar jika Anda melihat sesuatu sekali . Jika Anda melihat masalah pada lebih dari satu sampel, itu nyata.

PS Ini juga dapat dilakukan pada program multi-utas jika ada cara untuk mengumpulkan sampel panggilan-tumpukan dari kumpulan utas pada suatu titik waktu, seperti yang ada di Jawa.

PPS Sebagai generalitas kasar, semakin banyak lapisan abstraksi yang Anda miliki dalam perangkat lunak Anda, semakin besar kemungkinan Anda akan menemukan bahwa itu adalah penyebab masalah kinerja (dan peluang untuk mendapatkan peningkatan kecepatan).

Ditambahkan : Ini mungkin tidak jelas, tetapi teknik stack sampling bekerja sama baiknya di hadapan rekursi. Alasannya adalah bahwa waktu yang akan disimpan dengan menghapus instruksi diperkirakan oleh fraksi sampel yang mengandungnya, terlepas dari berapa kali itu dapat terjadi dalam sampel.

Keberatan lain yang sering saya dengar adalah: " Ini akan berhenti di suatu tempat secara acak, dan itu akan kehilangan masalah sebenarnya ". Ini berasal dari memiliki konsep sebelumnya tentang apa masalah sebenarnya. Properti utama dari masalah kinerja adalah bahwa mereka menentang harapan. Sampling memberi tahu Anda bahwa ada masalah, dan reaksi pertama Anda adalah tidak percaya. Itu wajar, tetapi Anda bisa yakin jika menemukan masalah itu nyata, dan sebaliknya.

Ditambahkan : Biarkan saya membuat penjelasan Bayesian tentang cara kerjanya. Misalkan ada beberapa instruksi I(panggilan atau lainnya) yang ada di panggilan menumpuk sebagian fwaktu (dan dengan demikian biaya yang banyak). Untuk kesederhanaan, anggaplah kita tidak tahu apa fitu, tetapi anggaplah itu adalah 0,1, 0,2, 0,3, ... 0,9, 1,0, dan probabilitas sebelumnya dari masing-masing kemungkinan ini adalah 0,1, sehingga semua biaya ini kemungkinan sama besarnya a-priori.

Kemudian anggaplah kita mengambil hanya 2 tumpukan sampel, dan kita melihat instruksi Ipada kedua sampel, pengamatan yang ditunjuk o=2/2. Ini memberi kita perkiraan baru dari frekuensi fdari I, menurut ini:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&&f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.1    1     1             0.1          0.1            0.25974026
0.1    0.9   0.81          0.081        0.181          0.47012987
0.1    0.8   0.64          0.064        0.245          0.636363636
0.1    0.7   0.49          0.049        0.294          0.763636364
0.1    0.6   0.36          0.036        0.33           0.857142857
0.1    0.5   0.25          0.025        0.355          0.922077922
0.1    0.4   0.16          0.016        0.371          0.963636364
0.1    0.3   0.09          0.009        0.38           0.987012987
0.1    0.2   0.04          0.004        0.384          0.997402597
0.1    0.1   0.01          0.001        0.385          1

                  P(o=2/2) 0.385                

Kolom terakhir mengatakan bahwa, misalnya, probabilitas bahwa f> = 0,5 adalah 92%, naik dari asumsi sebelumnya sebesar 60%.

Misalkan asumsi sebelumnya berbeda. Misalkan kita anggap P(f=0.1)0,991 (hampir pasti), dan semua kemungkinan lain hampir tidak mungkin (0,001). Dengan kata lain, kepastian kami sebelumnya adalah yang Imurah. Lalu kita dapatkan:

Prior                                    
P(f=x) x  P(o=2/2|f=x) P(o=2/2&& f=x)  P(o=2/2&&f >= x)  P(f >= x | o=2/2)

0.001  1    1              0.001        0.001          0.072727273
0.001  0.9  0.81           0.00081      0.00181        0.131636364
0.001  0.8  0.64           0.00064      0.00245        0.178181818
0.001  0.7  0.49           0.00049      0.00294        0.213818182
0.001  0.6  0.36           0.00036      0.0033         0.24
0.001  0.5  0.25           0.00025      0.00355        0.258181818
0.001  0.4  0.16           0.00016      0.00371        0.269818182
0.001  0.3  0.09           0.00009      0.0038         0.276363636
0.001  0.2  0.04           0.00004      0.00384        0.279272727
0.991  0.1  0.01           0.00991      0.01375        1

                  P(o=2/2) 0.01375                

Sekarang dikatakan P(f >= 0.5)26%, naik dari asumsi sebelumnya 0,6%. Jadi Bayes memungkinkan kami memperbarui perkiraan biaya yang mungkin terjadi I. Jika jumlah data kecil, itu tidak memberi tahu kami secara akurat berapa biayanya, hanya bahwa itu cukup besar untuk diperbaiki.

Namun cara lain untuk melihatnya disebut Aturan Suksesi . Jika Anda melempar koin 2 kali, dan muncul dua kali di kepala, apa artinya itu memberi tahu Anda tentang kemungkinan bobot koin? Cara yang dihormati untuk menjawab adalah dengan mengatakan bahwa itu adalah distribusi Beta, dengan nilai rata-rata (number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%.

(Kuncinya adalah bahwa kita melihat Ilebih dari sekali. Jika kita hanya melihatnya sekali, itu tidak memberi tahu kita banyak kecuali bahwa f> 0.)

Jadi, bahkan sejumlah kecil sampel dapat memberi tahu kami banyak tentang biaya instruksi yang dilihatnya. (Dan itu akan melihat mereka dengan frekuensi, rata-rata, sebanding dengan biaya mereka. Jika nsampel yang diambil, dan fbiaya, maka Iakan muncul di nf+/-sqrt(nf(1-f))sampel. Contoh, n=10, f=0.3, yang 3+/-1.4sampel.)


Ditambahkan : Untuk memberikan nuansa intuitif untuk perbedaan antara pengukuran dan pengambilan sampel tumpukan acak:
Ada profiler sekarang yang mengambil sampel tumpukan, bahkan pada waktu jam dinding, tetapi yang keluar adalah pengukuran (atau jalur panas, atau hot spot, dari mana "bottleneck" dapat dengan mudah disembunyikan). Apa yang mereka tidak perlihatkan kepada Anda (dan mereka dengan mudah bisa) adalah sampel yang sebenarnya. Dan jika tujuan Anda adalah menemukan kemacetan, jumlah yang perlu Anda lihat adalah, rata-rata , 2 dibagi dengan sebagian kecil dari waktu yang dibutuhkan. Jadi jika dibutuhkan 30% waktu, 2 / 0,3 = 6,7 sampel, rata-rata, akan menunjukkannya, dan kemungkinan 20 sampel akan menunjukkan itu adalah 99,2%.

Berikut ini adalah ilustrasi off-the-cuff perbedaan antara memeriksa pengukuran dan memeriksa sampel tumpukan. Kemacetan bisa jadi satu gumpalan besar seperti ini, atau banyak gumpalan kecil, tidak ada bedanya.

masukkan deskripsi gambar di sini

Pengukuran horisontal; ia memberi tahu Anda berapa lama waktu yang dibutuhkan untuk rutinitas khusus. Pengambilan sampel adalah vertikal. Jika ada cara untuk menghindari apa yang dilakukan seluruh program pada saat itu, dan jika Anda melihatnya pada sampel kedua , Anda telah menemukan hambatannya. Itulah yang membuat perbedaan - melihat seluruh alasan waktu yang dihabiskan, bukan hanya berapa banyak.


292
Ini pada dasarnya adalah profiler pengambilan sampel orang miskin, yang bagus, tetapi Anda berisiko mengambil ukuran sampel terlalu kecil yang mungkin akan memberi Anda hasil yang sepenuhnya palsu.
Crashworks

100
@ Crash: Saya tidak akan memperdebatkan bagian "orang miskin" :-) Memang benar bahwa pengukuran presisi statistik membutuhkan banyak sampel, tetapi ada dua tujuan yang saling bertentangan - pengukuran dan lokasi masalah. Saya fokus pada yang terakhir, yang Anda butuhkan ketepatan lokasi, bukan ketepatan ukuran. Jadi misalnya, mungkin ada, tumpukan tengah, panggilan fungsi tunggal A (); yang menyumbang 50% dari waktu, tetapi bisa dalam fungsi besar B lainnya, bersama dengan banyak panggilan lain ke A () yang tidak mahal. Ringkasan waktu fungsi yang tepat bisa menjadi petunjuk, tetapi setiap sampel tumpukan lainnya akan menunjukkan masalah.
Mike Dunlavey

41
... dunia tampaknya berpikir bahwa grafik panggilan, yang dianotasi dengan jumlah panggilan dan / atau waktu rata-rata, cukup baik. Bukan itu. Dan bagian yang menyedihkan adalah, bagi mereka yang mencicipi tumpukan panggilan, informasi yang paling berguna tepat di depan mereka, tetapi mereka membuangnya, demi "statistik".
Mike Dunlavey

30
Saya tidak bermaksud untuk tidak setuju dengan teknik Anda. Jelas saya sangat mengandalkan profiler sampling stack-walking. Saya hanya menunjukkan bahwa ada beberapa alat yang melakukannya secara otomatis sekarang, yang penting ketika Anda melewati titik mendapatkan fungsi dari 25% hingga 15% dan perlu merobohkannya dari 1,2% menjadi 0,6%.
Crashworks

13
-1: Ide bagus, tetapi jika Anda dibayar untuk bekerja di lingkungan yang berorientasi kinerja sekalipun, ini adalah buang-buang waktu semua orang. Gunakan profiler asli sehingga kami tidak perlu ikut serta dan memperbaiki masalah yang sebenarnya.
Sam Harwell

583

Anda dapat menggunakan Valgrind dengan opsi berikut

valgrind --tool=callgrind ./(Your binary)

Ini akan menghasilkan file bernama callgrind.out.x. Anda kemudian dapat menggunakan kcachegrindalat untuk membaca file ini. Ini akan memberi Anda analisis grafis dari hal-hal dengan hasil seperti garis mana yang menghabiskan biaya.


51
Valgrind besar, tetapi memperingatkan bahwa hal itu akan membuat sialan program anda lambat
Neves

30
Lihat juga Gprof2Dot untuk cara alternatif yang menakjubkan untuk memvisualisasikan output. ./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
Sebastian

2
@neves Ya Valgrind tidak begitu membantu dalam hal kecepatan untuk membuat profil aplikasi "gstreamer" dan "opencv" secara real-time.
Antusiasme

1
stackoverflow.com/questions/375913/… adalah solusi parsial untuk masalah kecepatan.
Tõnu Samuel

3
@Sebastian: gprof2dotada di sini sekarang: github.com/jrfonseca/gprof2dot
John Zwinck

348

Saya menganggap Anda menggunakan GCC. Solusi standar adalah profil dengan gprof .

Pastikan untuk menambah -pgkompilasi sebelum membuat profil:

cc -o myprog myprog.c utils.c -g -pg

Saya belum mencobanya tetapi saya pernah mendengar hal-hal baik tentang google-perftools . Ini pasti patut dicoba.

Pertanyaan terkait di sini .

Beberapa kata kunci lain jika gproftidak melakukan pekerjaan untuk Anda: Valgrind , Intel VTune , Sun DTrace .


3
Saya setuju bahwa gprof adalah standar saat ini. Sekadar catatan, Valgrind digunakan untuk kebocoran memori profil dan aspek terkait memori lainnya dari program Anda, bukan untuk optimasi kecepatan.
Bill the Lizard

68
Bill, Dalam paket vaglrind Anda dapat menemukan callgrind dan massif. Keduanya cukup berguna untuk aplikasi profil
dario minonne

7
@ Bill-the-Lizard: Beberapa komentar di gprof : stackoverflow.com/questions/1777556/alternatives-to-gprof/…
Mike Dunlavey

6
gprof -pg hanya merupakan perkiraan profil panggilan balik. Ini menyisipkan panggilan mcount untuk melacak fungsi mana yang memanggil fungsi lain. Ini menggunakan pengambilan sampel berdasarkan waktu standar untuk, eh, waktu. Ini kemudian membagi kali sampel dalam suatu fungsi foo () kembali ke penelepon foo (), sesuai dengan jumlah panggilan. Jadi itu tidak membedakan antara panggilan biaya yang berbeda.
Krazy Glew

1
Dengan dentang / dentang ++, orang dapat mempertimbangkan untuk menggunakan profiler CPU gperftools . Peringatan: Belum melakukannya sendiri.
einpoklum

257

Kernel yang lebih baru (mis. Kernel Ubuntu terbaru) hadir dengan alat 'perf' baru ( apt-get install linux-tools) AKA perf_events .

Ini datang dengan profiler pengambilan sampel klasik ( halaman manual ) serta timechart yang mengagumkan !

Yang penting adalah bahwa alat-alat ini dapat berupa sistem profiling dan bukan hanya proses profiling - mereka dapat menunjukkan interaksi antara utas, proses dan kernel dan membiarkan Anda memahami penjadwalan dan dependensi I / O antara proses.

Teks alternatif


12
Alat hebat! Apakah ada cara bagi saya untuk mendapatkan tampilan "kupu-kupu" khas yang dimulai dari gaya "main-> func1-> fun2"? Sepertinya saya tidak bisa mengetahuinya ... perf reportsepertinya memberi saya nama fungsi dengan orang tua panggilan ... (jadi ini semacam tampilan kupu-kupu terbalik)
kizzx2

Will, dapat menampilkan timechart aktivitas utas; dengan informasi nomor CPU ditambahkan? Saya ingin melihat kapan dan utas mana yang berjalan pada setiap CPU.
osgx

2
@ kizzx2 - Anda dapat menggunakan gprof2dotdan perf script. Alat yang sangat bagus!
dashesy

2
Kernel yang lebih baru seperti 4.13 memiliki eBPF untuk pembuatan profil. Lihat brendangregg.com/blog/2015-05-15/ebpf-one-small-step.html dan brendangregg.com/ebpf.html
Andrew Stern

Pengantar bagus lainnya perfada di archive.li/9r927#selection-767.126-767.271 (Mengapa para dewa SO memutuskan untuk menghapus halaman itu dari basis pengetahuan SO adalah di luar saya ....)
ragerdl

75

Saya akan menggunakan Valgrind dan Callgrind sebagai dasar untuk rangkaian alat profiling saya. Yang penting diketahui adalah bahwa Valgrind pada dasarnya adalah Mesin Virtual:

(wikipedia) Valgrind pada dasarnya adalah mesin virtual yang menggunakan teknik kompilasi just-in-time (JIT), termasuk kompilasi dinamis. Tidak ada dari program asli yang dapat dijalankan langsung pada prosesor host. Sebagai gantinya, Valgrind pertama menerjemahkan program ke dalam bentuk sementara, lebih sederhana yang disebut Intermediate Representation (IR), yang merupakan bentuk netral-prosesor, berbasis SSA. Setelah konversi, alat (lihat di bawah) bebas untuk melakukan transformasi apa pun yang diinginkannya pada IR, sebelum Valgrind menerjemahkan kembali IR ke dalam kode mesin dan membiarkan prosesor host menjalankannya.

Callgrind adalah profiler yang dibangun di atas itu. Manfaat utama adalah Anda tidak perlu menjalankan aplikasi selama berjam-jam untuk mendapatkan hasil yang andal. Bahkan satu run kedua sudah cukup untuk mendapatkan hasil yang solid, andal, karena Callgrind adalah profiler non-probing .

Alat lain yang dibangun di atas Valgrind adalah Massif. Saya menggunakannya untuk penggunaan memori tumpukan profil. Ini bekerja dengan baik. Apa yang dilakukannya adalah memberi Anda snapshot dari penggunaan memori - informasi terperinci APA yang memegang persentase memori APA, dan WHO telah meletakkannya di sana. Informasi tersebut tersedia di berbagai titik waktu aplikasi dijalankan.


70

Jawaban untuk menjalankan valgrind --tool=callgrindtidak cukup lengkap tanpa beberapa opsi. Kami biasanya tidak ingin profil 10 menit waktu startup lambat di bawah Valgrind dan ingin profil program kami ketika sedang melakukan beberapa tugas.

Jadi ini yang saya rekomendasikan. Jalankan program terlebih dahulu:

valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no ./binary > tmp

Sekarang ketika ia berfungsi dan kami ingin memulai pembuatan profil, kami harus menjalankannya di jendela lain:

callgrind_control -i on

Ini mengaktifkan profil. Untuk mematikannya dan menghentikan seluruh tugas yang mungkin kita gunakan:

callgrind_control -k

Sekarang kami memiliki beberapa file bernama callgrind.out. * Di direktori saat ini. Untuk melihat hasil pembuatan profil, gunakan:

kcachegrind callgrind.out.*

Saya sarankan di jendela berikutnya untuk mengklik tajuk kolom "Cukup", jika tidak, ini menunjukkan bahwa "main ()" adalah tugas yang paling memakan waktu. "Diri" menunjukkan seberapa banyak masing-masing fungsi itu sendiri memakan waktu, tidak bersama-sama dengan tanggungan.


9
Sekarang dengan alasan tertentu file callgrind.out. * Selalu kosong. Menjalankan callgrind_control -d berguna untuk memaksa dump data ke disk.
Tõnu Samuel

3
Tidak bisa Konteks saya yang biasa adalah sesuatu seperti seluruh MySQL atau PHP atau hal besar serupa. Seringkali bahkan tidak tahu apa yang ingin saya pisahkan pada awalnya.
T Samuelnu Samuel

2
Atau dalam kasus saya, program saya sebenarnya memuat banyak data ke dalam cache LRU, dan saya ingin tidak membuat profil itu. Jadi saya paksa memuat subset cache saat startup, dan profil kode hanya menggunakan data itu (membiarkan OS + CPU mengelola penggunaan memori dalam cache saya). Ini bekerja, tetapi memuat cache itu lambat dan intensif CPU di seluruh kode yang saya coba profil dalam konteks yang berbeda, jadi callgrind menghasilkan hasil yang sangat tercemar.
Kode Abominator

2
ada juga CALLGRIND_TOGGLE_COLLECTuntuk mengaktifkan / menonaktifkan koleksi secara terprogram; lihat stackoverflow.com/a/13700817/288875
Andre Holzner

1
Wow, saya tidak tahu ini ada, terima kasih!
Vincent Fourmond

59

Ini adalah jawaban atas jawaban Gprof Nazgob .

Saya telah menggunakan Gprof beberapa hari terakhir dan telah menemukan tiga batasan signifikan, salah satunya saya belum melihat didokumentasikan di tempat lain (belum):

  1. Itu tidak bekerja dengan baik pada kode multi-threaded, kecuali jika Anda menggunakan solusi

  2. Grafik panggilan menjadi bingung oleh pointer fungsi. Contoh: Saya memiliki fungsi yang dipanggil multithread()yang memungkinkan saya untuk melakukan multi-utas fungsi yang ditentukan pada array yang ditentukan (keduanya dilewatkan sebagai argumen). Namun, lihat semua panggilan multithread()sebagai setara untuk keperluan menghitung waktu yang dihabiskan pada anak-anak. Karena beberapa fungsi yang saya multithread()anggap lebih lama dari yang lain, grafik panggilan saya sebagian besar tidak berguna. (Bagi mereka yang bertanya-tanya apakah threading adalah masalah di sini: tidak, multithread()bisa opsional, dan memang dalam hal ini, jalankan semuanya secara berurutan pada utas panggilan saja).

  3. Dikatakan di sini bahwa "... jumlah angka panggilan diperoleh dengan menghitung, bukan pengambilan sampel. Angka tersebut sepenuhnya akurat ...". Namun saya menemukan grafik panggilan saya memberi saya 5345859132 + 784984078 sebagai statistik panggilan untuk fungsi saya yang paling dipanggil, di mana nomor pertama seharusnya menjadi panggilan langsung, dan panggilan rekursif kedua (yang semuanya dari dirinya sendiri). Karena ini tersirat saya punya bug, saya memasukkan penghitung panjang (64-bit) ke dalam kode dan melakukan hal yang sama lagi. Hitungan saya: 5345859132 langsung, dan 78094395406 panggilan rekursif diri. Ada banyak digit di sana, jadi saya akan menunjukkan panggilan rekursif yang saya ukur adalah 78bn, dibandingkan 784m dari Gprof: faktor 100 berbeda. Keduanya berjalan adalah kode berulir tunggal dan tidak dioptimalkan, satu dikompilasi -gdan yang lainnya -pg.

Ini adalah GNU Gprof (GNU Binutils for Debian) 2.18.0.20080103 berjalan di bawah Debian Lenny 64-bit, jika itu membantu siapa pun.


Ya, itu memang sampel, tetapi tidak untuk angka panggilan. Menariknya, mengikuti tautan Anda pada akhirnya membawa saya ke versi terbaru dari halaman buku panduan yang saya tautkan dalam posting saya, URL baru: sourceware.org/binutils/docs/gprof/… Ini mengulangi kutipan di bagian (iii) dari jawaban saya, tetapi juga mengatakan "Dalam aplikasi multi-utas, atau aplikasi utas tunggal yang ditautkan dengan pustaka multi-utas, penghitungan hanya deterministik jika fungsi penghitungan aman-thread. (Catatan: berhati-hatilah bahwa fungsi penghitungan mcount di glibc bukan thread -aman)."
Rob_before_edits

Tidak jelas bagi saya jika ini menjelaskan hasil saya di (iii). Kode saya ditautkan -lpthread -lm dan mendeklarasikan sebagai "pthread_t * thr" dan "pthread_mutex_t nextLock = PTHREAD_MUTEX_INITIALIZER" variabel statis bahkan ketika sedang menjalankan single threaded. Saya biasanya menganggap bahwa "tautan dengan perpustakaan multi-utas" berarti benar-benar menggunakan perpustakaan itu, dan pada tingkat yang lebih besar dari ini, tetapi saya bisa saja salah!
Rob_before_edits

23

Gunakan Valgrind, callgrind, dan kcachegrind:

valgrind --tool=callgrind ./(Your binary)

menghasilkan callgrind.out.x. Baca ini menggunakan kcachegrind.

Gunakan gprof (tambahkan -pg):

cc -o myprog myprog.c utils.c -g -pg 

(tidak begitu baik untuk multi-utas, pointer fungsi)

Gunakan google-perftools:

Menggunakan sampling waktu, I / O dan kemacetan CPU terungkap.

Intel VTune adalah yang terbaik (gratis untuk tujuan pendidikan).

Lainnya: AMD Codeanalyst (sejak diganti dengan AMD CodeXL), OProfile, alat 'perf' (apt-get install linux-tools)


11

Survei teknik profiling C ++

Dalam jawaban ini, saya akan menggunakan beberapa alat berbeda untuk menganalisis beberapa program pengujian yang sangat sederhana, untuk membandingkan secara konkret cara kerja alat tersebut.

Program pengujian berikut ini sangat sederhana dan melakukan hal berikut:

  • mainpanggilan fastdan maybe_slow3 kali, salah satu maybe_slowpanggilan menjadi lambat

    Panggilan lambat maybe_slowadalah 10x lebih lama, dan mendominasi runtime jika kami mempertimbangkan panggilan ke fungsi anak common. Idealnya, alat profiling akan dapat mengarahkan kita ke panggilan lambat tertentu.

  • keduanya fastdan maybe_slowpanggilan common, yang menyumbang sebagian besar pelaksanaan program

  • Antarmuka program adalah:

    ./main.out [n [seed]]

    dan program ini melakukan O(n^2)loop secara total. seedhanya untuk mendapatkan output yang berbeda tanpa mempengaruhi runtime.

main.c

#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

uint64_t __attribute__ ((noinline)) common(uint64_t n, uint64_t seed) {
    for (uint64_t i = 0; i < n; ++i) {
        seed = (seed * seed) - (3 * seed) + 1;
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) fast(uint64_t n, uint64_t seed) {
    uint64_t max = (n / 10) + 1;
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

uint64_t __attribute__ ((noinline)) maybe_slow(uint64_t n, uint64_t seed, int is_slow) {
    uint64_t max = n;
    if (is_slow) {
        max *= 10;
    }
    for (uint64_t i = 0; i < max; ++i) {
        seed = common(n, (seed * seed) - (3 * seed) + 1);
    }
    return seed;
}

int main(int argc, char **argv) {
    uint64_t n, seed;
    if (argc > 1) {
        n = strtoll(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    if (argc > 2) {
        seed = strtoll(argv[2], NULL, 0);
    } else {
        seed = 0;
    }
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 1);
    seed += fast(n, seed);
    seed += maybe_slow(n, seed, 0);
    seed += fast(n, seed);
    printf("%" PRIX64 "\n", seed);
    return EXIT_SUCCESS;
}

gprof

gprof membutuhkan kompilasi ulang perangkat lunak dengan instrumentasi, dan itu juga menggunakan pendekatan pengambilan sampel bersama dengan instrumentasi itu. Karena itu, ia menemukan keseimbangan antara akurasi (pengambilan sampel tidak selalu sepenuhnya akurat dan dapat melewati fungsi) dan pelambatan eksekusi (instrumentasi dan pengambilan sampel adalah teknik yang relatif cepat yang tidak terlalu memperlambat eksekusi).

gprof terintegrasi dengan GCC / binutils, jadi yang harus kita lakukan adalah mengkompilasi dengan -pgopsi untuk mengaktifkan gprof. Kami kemudian menjalankan program secara normal dengan parameter CLI ukuran yang menghasilkan jangka waktu yang wajar beberapa detik ( 10000):

gcc -pg -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time ./main.out 10000

Untuk alasan pendidikan, kami juga akan menjalankan tanpa mengaktifkan optimisasi. Perhatikan bahwa ini tidak berguna dalam praktik, karena Anda biasanya hanya peduli untuk mengoptimalkan kinerja program yang dioptimalkan:

gcc -pg -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out 10000

Pertama, timeberi tahu kami bahwa waktu eksekusi dengan dan tanpa -pgsama, yang hebat: tidak ada perlambatan! Namun saya telah melihat akun perlambatan 2x - 3x pada perangkat lunak yang kompleks, misalnya seperti yang ditunjukkan dalam tiket ini .

Karena kami kompilasi dengan -pg, menjalankan program menghasilkan file gmon.outfile yang berisi data profil.

Kita dapat mengamati file itu secara grafis dengan gprof2dotseperti yang ditanyakan di: Apakah mungkin untuk mendapatkan representasi grafis dari hasil gprof?

sudo apt install graphviz
python3 -m pip install --user gprof2dot
gprof main.out > main.gprof
gprof2dot < main.gprof | dot -Tsvg -o output.svg

Di sini, gprofalat membaca gmon.outinformasi jejak, dan menghasilkan laporan yang dapat dibaca manusia main.gprof, yang gprof2dotkemudian dibaca untuk menghasilkan grafik.

Sumber untuk gprof2dot ada di: https://github.com/jrfonseca/gprof2dot

Kami mengamati hal-hal berikut untuk -O0pelarian:

masukkan deskripsi gambar di sini

dan untuk -O3pelarian:

masukkan deskripsi gambar di sini

The -O0output cukup banyak jelas. Sebagai contoh, ini menunjukkan bahwa 3 maybe_slowpanggilan dan panggilan anak mereka mengambil 97,56% dari total runtime, meskipun eksekusi maybe_slowitu sendiri tanpa anak-anak menyumbang 0,00% dari total waktu eksekusi, yaitu hampir semua waktu yang dihabiskan dalam fungsi tersebut dihabiskan untuk panggilan anak.

TODO: mengapa mainhilang dari -O3output, meskipun saya bisa melihatnya di btdalam GDB? Fungsi yang hilang dari keluaran GProf saya pikir itu karena gprof juga berdasarkan sampel selain instrumentasi yang dikompilasi, dan -O3 mainterlalu cepat dan tidak punya sampel.

Saya memilih output SVG daripada PNG karena SVG dapat dicari dengan Ctrl + F dan ukuran file bisa sekitar 10x lebih kecil. Selain itu, lebar dan tinggi gambar yang dihasilkan dapat menjadi sangat muda dengan puluhan ribu piksel untuk perangkat lunak yang kompleks, dan eogbug GNOME 3.28.1 keluar dalam hal itu untuk PNG, sementara SVG dibuka oleh browser saya secara otomatis. gimp 2.8 bekerja dengan baik, lihat juga:

tetapi meskipun demikian, Anda akan banyak menyeret gambar di sekitar untuk menemukan apa yang Anda inginkan, lihat misalnya gambar ini dari contoh perangkat lunak "nyata" yang diambil dari tiket ini :

masukkan deskripsi gambar di sini

Dapatkah Anda menemukan tumpukan panggilan yang paling penting dengan mudah dengan semua garis spaghetti kecil yang tidak disortir saling bertabrakan? Mungkin ada dotopsi yang lebih baik saya yakin, tetapi saya tidak ingin pergi ke sana sekarang. Yang benar-benar kita butuhkan adalah pemirsa khusus yang berdedikasi untuk itu, tetapi saya belum menemukannya:

Namun Anda dapat menggunakan peta warna untuk sedikit mengurangi masalah tersebut. Misalnya, pada gambar besar sebelumnya, saya akhirnya berhasil menemukan jalur kritis di sebelah kiri ketika saya membuat deduksi cemerlang bahwa hijau muncul setelah merah, diikuti akhirnya dengan biru yang lebih gelap dan lebih gelap.

Atau, kita juga dapat mengamati keluaran teks dari gprofalat binutils bawaan yang sebelumnya kita simpan di:

cat main.gprof

Secara default, ini menghasilkan output yang sangat verbose yang menjelaskan apa artinya data output. Karena saya tidak bisa menjelaskan lebih baik dari itu, saya akan membiarkan Anda membacanya sendiri.

Setelah Anda memahami format output data, Anda dapat mengurangi verbositas untuk hanya menampilkan data tanpa tutorial dengan -bopsi:

gprof -b main.out

Dalam contoh kami, output untuk -O0:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls   s/call   s/call  name    
100.35      3.67     3.67   123003     0.00     0.00  common
  0.00      3.67     0.00        3     0.00     0.03  fast
  0.00      3.67     0.00        3     0.00     1.19  maybe_slow

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.27% of 3.67 seconds

index % time    self  children    called     name
                0.09    0.00    3003/123003      fast [4]
                3.58    0.00  120000/123003      maybe_slow [3]
[1]    100.0    3.67    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]    100.0    0.00    3.67                 main [2]
                0.00    3.58       3/3           maybe_slow [3]
                0.00    0.09       3/3           fast [4]
-----------------------------------------------
                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]
-----------------------------------------------
                0.00    0.09       3/3           main [2]
[4]      2.4    0.00    0.09       3         fast [4]
                0.09    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common                  [4] fast                    [3] maybe_slow

dan untuk -O3:

Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
100.52      1.84     1.84   123003    14.96    14.96  common

            Call graph


granularity: each sample hit covers 2 byte(s) for 0.54% of 1.84 seconds

index % time    self  children    called     name
                0.04    0.00    3003/123003      fast [3]
                1.79    0.00  120000/123003      maybe_slow [2]
[1]    100.0    1.84    0.00  123003         common [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     97.6    0.00    1.79                 maybe_slow [2]
                1.79    0.00  120000/123003      common [1]
-----------------------------------------------
                                                 <spontaneous>
[3]      2.4    0.00    0.04                 fast [3]
                0.04    0.00    3003/123003      common [1]
-----------------------------------------------

Index by function name

   [1] common

Sebagai ringkasan yang sangat cepat untuk setiap bagian, misalnya:

                0.00    3.58       3/3           main [2]
[3]     97.6    0.00    3.58       3         maybe_slow [3]
                3.58    0.00  120000/123003      common [1]

berpusat di sekitar fungsi yang dibiarkan berlekuk ( maybe_flow). [3]adalah ID dari fungsi itu. Di atas fungsi, ada peneleponnya, dan di bawahnya ada callees.

Untuk -O3, lihat di sini seperti pada keluaran grafis itu maybe_slowdan fasttidak memiliki orangtua yang dikenal, yang merupakan arti dari dokumentasi itu <spontaneous>.

Saya tidak yakin apakah ada cara yang baik untuk melakukan profil garis-demi-baris dengan gprof: `gprof` waktu yang dihabiskan dalam baris kode tertentu

valgrind callgrind

valgrind menjalankan program melalui mesin virtual valgrind. Ini membuat profil sangat akurat, tetapi juga menghasilkan perlambatan program yang sangat besar. Saya juga telah menyebutkan kcachegrind sebelumnya di: Alat untuk mendapatkan grafik panggilan fungsi bergambar kode

callgrind adalah alat valgrind untuk kode profil dan kcachegrind adalah program KDE yang dapat memvisualisasikan output cachegrind.

Pertama kita harus menghapus -pgflag untuk kembali ke kompilasi normal, jika tidak run sebenarnya gagal Profiling timer expired, dan ya, ini sangat umum yang saya lakukan dan ada pertanyaan Stack Overflow untuk itu.

Jadi kami mengkompilasi dan menjalankan sebagai:

sudo apt install kcachegrind valgrind
gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
time valgrind --tool=callgrind valgrind --dump-instr=yes \
  --collect-jumps=yes ./main.out 10000

Saya mengaktifkan --dump-instr=yes --collect-jumps=yeskarena ini juga membuang informasi yang memungkinkan kami melihat gangguan kinerja per jalur perakitan, dengan biaya overhead tambahan yang relatif kecil.

Off the bat, timememberitahu kami bahwa program ini membutuhkan waktu 29,5 detik untuk dieksekusi, jadi kami mengalami penurunan sekitar 15x pada contoh ini. Jelas, perlambatan ini akan menjadi batasan serius untuk beban kerja yang lebih besar. Pada "contoh perangkat lunak dunia nyata" yang disebutkan di sini , saya mengamati penurunan 80x.

Proses menghasilkan file data profil bernama callgrind.out.<pid>eg callgrind.out.8554dalam kasus saya. Kami melihat file itu dengan:

kcachegrind callgrind.out.8554

yang menunjukkan GUI yang berisi data yang mirip dengan output gprof tekstual:

masukkan deskripsi gambar di sini

Juga, jika kita pergi ke kanan bawah "Grafik Panggilan" tab, kita melihat grafik panggilan yang dapat kita ekspor dengan mengklik kanan untuk mendapatkan gambar berikut dengan jumlah batas putih yang tidak masuk akal :-)

masukkan deskripsi gambar di sini

Saya pikir fasttidak muncul pada grafik itu karena kcachegrind pasti telah menyederhanakan visualisasi karena panggilan itu memakan waktu terlalu sedikit, ini kemungkinan akan menjadi perilaku yang Anda inginkan pada program nyata. Menu klik kanan memiliki beberapa pengaturan untuk mengontrol kapan harus menyisihkan node seperti itu, tetapi saya tidak bisa membuatnya untuk menampilkan panggilan singkat setelah upaya cepat. Jika saya mengklik pada fastjendela kiri, itu memang menampilkan grafik panggilan fast, sehingga tumpukan itu benar-benar ditangkap. Belum ada yang menemukan cara untuk menampilkan grafik panggilan grafik lengkap: Buat panggilan panggil tunjukkan semua panggilan fungsi di kcachegrind callgraph

TODO pada perangkat lunak C ++ yang kompleks, saya melihat beberapa entri tipe <cycle N>, misalnya di <cycle 11>mana saya mengharapkan nama fungsi, apa artinya itu? Saya perhatikan ada tombol "Deteksi Siklus" untuk menghidupkan dan mematikannya, tetapi apa artinya?

perf dari linux-tools

perftampaknya menggunakan mekanisme pengambilan sampel kernel Linux secara eksklusif. Ini membuatnya sangat mudah untuk diatur, tetapi juga tidak sepenuhnya akurat.

sudo apt install linux-tools
time perf record -g ./main.out 10000

Ini menambahkan 0,2s ke eksekusi, jadi kami baik-baik saja waktu, tapi saya masih tidak melihat banyak minat, setelah memperluas commonnode dengan panah kanan keyboard:

Samples: 7K of event 'cycles:uppp', Event count (approx.): 6228527608     
  Children      Self  Command   Shared Object     Symbol                  
-   99.98%    99.88%  main.out  main.out          [.] common              
     common                                                               
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.01%     0.01%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.01%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.01%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.01%     0.00%  main.out  ld-2.27.so        [.] mprotect            
     0.01%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.01%     0.00%  main.out  ld-2.27.so        [.] _xstat              
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x2f3d4f4944555453  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007fff3cfc57ac  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

Jadi saya mencoba membuat tolok ukur -O0program untuk melihat apakah itu menunjukkan sesuatu, dan hanya sekarang, akhirnya, saya melihat grafik panggilan:

Samples: 15K of event 'cycles:uppp', Event count (approx.): 12438962281   
  Children      Self  Command   Shared Object     Symbol                  
+   99.99%     0.00%  main.out  [unknown]         [.] 0x04be258d4c544155  
+   99.99%     0.00%  main.out  libc-2.27.so      [.] __libc_start_main   
-   99.99%     0.00%  main.out  main.out          [.] main                
   - main                                                                 
      - 97.54% maybe_slow                                                 
           common                                                         
      - 2.45% fast                                                        
           common                                                         
+   99.96%    99.85%  main.out  main.out          [.] common              
+   97.54%     0.03%  main.out  main.out          [.] maybe_slow          
+    2.45%     0.00%  main.out  main.out          [.] fast                
     0.11%     0.11%  main.out  [kernel]          [k] 0xffffffff8a6009e7  
     0.00%     0.00%  main.out  [unknown]         [k] 0x0000000000000040  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_sysdep_start    
     0.00%     0.00%  main.out  ld-2.27.so        [.] dl_main             
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_lookup_symbol_x 
     0.00%     0.00%  main.out  [kernel]          [k] 0xffffffff8a600158  
     0.00%     0.00%  main.out  ld-2.27.so        [.] mmap64              
     0.00%     0.00%  main.out  ld-2.27.so        [.] _dl_map_object      
     0.00%     0.00%  main.out  ld-2.27.so        [.] __GI___tunables_init
     0.00%     0.00%  main.out  [unknown]         [.] 0x552e53555f6e653d  
     0.00%     0.00%  main.out  [unknown]         [.] 0x00007ffe1cf20fdb  
     0.00%     0.00%  main.out  ld-2.27.so        [.] _start              

TODO: apa yang terjadi pada -O3eksekusi? Apakah hanya itu maybe_slowdan fastterlalu cepat dan tidak mendapatkan sampel? Apakah ini bekerja dengan baik -O3pada program yang lebih besar yang membutuhkan waktu lebih lama untuk dijalankan? Apakah saya melewatkan beberapa opsi CLI? Saya menemukan tentang -Funtuk mengontrol frekuensi sampel dalam Hertz, tetapi saya mengubahnya hingga maks diizinkan secara default -F 39500(dapat ditingkatkan dengan sudo) dan saya masih tidak melihat panggilan yang jelas.

Satu hal keren tentang perfalat FlameGraph dari Brendan Gregg yang menampilkan pengaturan waktu panggilan dengan cara yang sangat rapi yang memungkinkan Anda untuk dengan cepat melihat panggilan besar. Alat ini tersedia di: https://github.com/brendangregg/FlameGraph dan juga disebutkan pada nya Perf tutorial di: http://www.brendangregg.com/perf.html#FlameGraphs Ketika aku berlari perftanpa sudoaku ERROR: No stack counts foundjadi untuk sekarang saya akan melakukannya dengan sudo:

git clone https://github.com/brendangregg/FlameGraph
sudo perf record -F 99 -g -o perf_with_stack.data ./main.out 10000
sudo perf script -i perf_with_stack.data | FlameGraph/stackcollapse-perf.pl | FlameGraph/flamegraph.pl > flamegraph.svg

tetapi dalam program sederhana seperti itu, keluarannya tidak mudah dipahami, karena kita tidak dapat dengan mudah melihat maybe_slowmaupun fastpada grafik itu:

masukkan deskripsi gambar di sini

Pada contoh yang lebih kompleks, menjadi jelas apa arti grafik:

masukkan deskripsi gambar di sini

TODO ada log [unknown]fungsi dalam contoh itu, mengapa begitu?

Antarmuka GUI perf lain yang mungkin layak termasuk:

  • Plugin Eclipse Trace Compass: https://www.eclipse.org/tracecompass/

    Tetapi ini memiliki kelemahan yaitu Anda harus terlebih dahulu mengonversi data ke Format Jejak Umum, yang dapat dilakukan dengan perf data --to-ctf, tetapi harus diaktifkan pada waktu membangun / memiliki perfcukup baru, yang keduanya tidak berlaku untuk perf di Ubuntu 18.04

  • https://github.com/KDAB/hotspot

    Kelemahan dari ini adalah bahwa sepertinya tidak ada paket Ubuntu, dan membangunnya membutuhkan Qt 5.10 sedangkan Ubuntu 18.04 adalah pada Qt 5.9.

gperftools

Sebelumnya disebut "Google Performance Tools", sumber: https://github.com/gperftools/gperftools Berbasis sampel.

Pertama instal gperftools dengan:

sudo apt install google-perftools

Kemudian, kita dapat mengaktifkan profiler CPU gperftools dalam dua cara: saat runtime, atau saat membangun.

Pada saat runtime, kita harus melewati set LD_PRELOADto point ke libprofiler.so, yang dapat Anda temukan locate libprofiler.so, misalnya di sistem saya:

gcc -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libprofiler.so \
  CPUPROFILE=prof.out ./main.out 10000

Atau, kita dapat membangun pustaka pada saat tautan, membagikan lewat LD_PRELOADsaat runtime:

gcc -Wl,--no-as-needed,-lprofiler,--as-needed -ggdb3 -O3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
CPUPROFILE=prof.out ./main.out 10000

Lihat juga: gperftools - file profil tidak dibuang

Cara terbaik untuk melihat data ini yang saya temukan sejauh ini adalah membuat pprof output dalam format yang sama dengan yang diambil oleh kcachegrind sebagai input (ya, alat Valgrind-project-viewer-tool) dan gunakan kcachegrind untuk melihat bahwa:

google-pprof --callgrind main.out prof.out  > callgrind.out
kcachegrind callgrind.out

Setelah berjalan dengan salah satu metode tersebut, kami mendapatkan prof.outfile data profil sebagai output. Kita dapat melihat file itu secara grafis sebagai SVG dengan:

google-pprof --web main.out prof.out

masukkan deskripsi gambar di sini

yang memberi sebagai grafik panggilan akrab seperti alat lain, tetapi dengan unit kikuk jumlah sampel daripada detik.

Atau, kita juga bisa mendapatkan beberapa data tekstual dengan:

google-pprof --text main.out prof.out

pemberian yang mana:

Using local file main.out.
Using local file prof.out.
Total: 187 samples
     187 100.0% 100.0%      187 100.0% common
       0   0.0% 100.0%      187 100.0% __libc_start_main
       0   0.0% 100.0%      187 100.0% _start
       0   0.0% 100.0%        4   2.1% fast
       0   0.0% 100.0%      187 100.0% main
       0   0.0% 100.0%      183  97.9% maybe_slow

Lihat juga: Cara menggunakan alat google perf

Diuji di Ubuntu 18.04, gprof2dot 2019.11.30, valgrind 3.13.0, perf 4.15.18, kernel Linux 4.15.0, FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b, gperftools 2.5-2.


2
Secara default, perf record menggunakan register penunjuk bingkai. Kompiler modern tidak mencatat alamat bingkai dan sebaliknya menggunakan register sebagai tujuan umum. Alternatifnya adalah mengkompilasi dengan -fno-omit-frame-pointerflag atau menggunakan alternatif lain: merekam dengan --call-graph "dwarf"atau --call-graph "lbr"tergantung pada skenario Anda.
Jorge Bellon

5

Untuk program berulir tunggal, Anda dapat menggunakan igprof , The Ignominous Profiler: https://igprof.org/ .

Ini adalah profiler pengambilan sampel, di sepanjang baris ... panjang ... jawaban oleh Mike Dunlavey, yang akan membungkus hasil dalam susunan pohon panggilan yang dapat dijelajahi, dijelaskan dengan waktu atau memori yang dihabiskan di setiap fungsi, baik kumulatif atau per-fungsi.


Ini terlihat menarik, tetapi gagal dikompilasi dengan GCC 9.2. (Debian / Sid) Saya membuat masalah di github.
Basile Starynkevitch

5

Juga layak disebutkan

  1. HPCToolkit ( http://hpctoolkit.org/ ) - Sumber terbuka, berfungsi untuk program paralel dan memiliki GUI yang dapat digunakan untuk melihat hasil dengan berbagai cara
  2. Intel VTune ( https://software.intel.com/en-us/vtune ) - Jika Anda memiliki kompiler intel, ini sangat bagus
  3. TAU ( http://www.cs.uoregon.edu/research/tau/home.php )

Saya telah menggunakan HPCToolkit dan VTune dan mereka sangat efektif dalam menemukan kutub panjang di tenda dan tidak perlu kode Anda untuk dikompilasi ulang (kecuali bahwa Anda harus menggunakan tipe -g -O atau RelWithDebInfo di CMake untuk mendapatkan output yang berarti) . Saya pernah mendengar TAU memiliki kemampuan yang serupa.


4

Ini adalah dua metode yang saya gunakan untuk mempercepat kode saya:

Untuk aplikasi yang terikat CPU:

  1. Gunakan profiler dalam mode DEBUG untuk mengidentifikasi bagian yang dipertanyakan dari kode Anda
  2. Kemudian beralih ke mode RELEASE dan komentar bagian yang dipertanyakan dari kode Anda (berhenti tanpa apa-apa) sampai Anda melihat perubahan dalam kinerja.

Untuk aplikasi terikat I / O:

  1. Gunakan profiler dalam mode RELEASE untuk mengidentifikasi bagian kode Anda yang dipertanyakan.

NB

Jika Anda tidak memiliki profiler, gunakan profiler orang miskin itu. Hit jeda saat debug aplikasi Anda. Sebagian besar suite pengembang akan bergabung dengan nomor baris yang dikomentari. Secara statistik Anda cenderung mendarat di wilayah yang mengonsumsi sebagian besar siklus CPU Anda.

Untuk CPU, alasan untuk membuat profil dalam mode DEBUG adalah karena jika Anda mencoba membuat profil dalam mode RELEASE , kompiler akan mengurangi matematika, loop vektorisasi, dan fungsi sebaris yang cenderung menggumpal kode Anda menjadi kekacauan yang tidak dapat dipetakan ketika dirakit. Kekacauan yang tidak dapat dipetakan berarti profiler Anda tidak akan dapat dengan jelas mengidentifikasi apa yang perlu waktu lama karena perakitan mungkin tidak sesuai dengan kode sumber yang sedang dioptimalkan . Jika Anda memerlukan kinerja (misalnya sensitif terhadap waktu) dari mode RELEASE , nonaktifkan fitur debugger yang diperlukan untuk menjaga kinerja yang dapat digunakan.

Untuk I / O-terikat, profiler masih dapat mengidentifikasi operasi I / O dalam mode RELEASE karena operasi I / O terkait secara eksternal dengan perpustakaan bersama (sebagian besar waktu) atau dalam kasus terburuk, akan menghasilkan sistem panggil vektor interupsi (yang juga mudah diidentifikasi oleh profiler).


2
+1 Metode orang miskin bekerja dengan baik untuk I / O terikat serta untuk CPU terikat, dan saya sarankan melakukan semua penyetelan kinerja dalam mode DEBUG. Setelah selesai menyetel, lalu nyalakan RELEASE. Ini akan membuat peningkatan jika program terikat dengan CPU dalam kode Anda. Berikut video singkat tapi kasar dari proses tersebut.
Mike Dunlavey

3
Saya tidak akan menggunakan build DEBUG untuk profil kinerja. Seringkali saya melihat bahwa bagian-bagian penting kinerja dalam mode DEBUG sepenuhnya dioptimalkan dalam mode rilis. Masalah lain adalah penggunaan menegaskan dalam kode debug yang menambah kebisingan pada kinerja.
gast128

3
Apakah Anda membaca posting saya sama sekali? "Jika Anda memerlukan kinerja (misalnya sensitif terhadap waktu) dari mode RELEASE, nonaktifkan fitur debugger yang diperlukan untuk menjaga kinerja yang dapat digunakan", "Kemudian beralih ke mode RELEASE dan komentar bagian yang dipertanyakan dari kode Anda (Stub tanpa apa-apa) sampai Anda melihat perubahan kinerja. "? Saya mengatakan memeriksa kemungkinan area masalah dalam mode debug dan memverifikasi masalah-masalah itu dalam mode rilis untuk menghindari jebakan yang Anda sebutkan.
seo


2

Anda dapat menggunakan kerangka kerja pencatatan seperti logurukarena ia menyertakan cap waktu dan total waktu kerja yang dapat digunakan dengan baik untuk membuat profil:


1

Di tempat kerja kami memiliki alat yang sangat bagus yang membantu kami memantau apa yang kami inginkan dalam hal penjadwalan. Ini telah berguna beberapa kali.

Ini dalam C ++ dan harus disesuaikan dengan kebutuhan Anda. Sayangnya saya tidak bisa membagikan kode, hanya konsep. Anda menggunakan volatilebuffer "besar" yang berisi cap waktu dan ID aktivitas yang dapat Anda buang post mortem atau setelah menghentikan sistem logging (dan misalnya, membuangnya ke file).

Anda mengambil buffer yang disebut besar dengan semua data dan antarmuka kecil mem-parsingnya dan menampilkan peristiwa dengan nama (nilai naik / turun) seperti osiloskop dengan warna (dikonfigurasi dalam .hppfile).

Anda menyesuaikan jumlah acara yang dihasilkan untuk fokus hanya pada apa yang Anda inginkan. Ini banyak membantu kami untuk masalah penjadwalan sambil mengonsumsi jumlah CPU yang kami inginkan berdasarkan jumlah peristiwa yang dicatat per detik.

Anda membutuhkan 3 file:

toolname.hpp // interface
toolname.cpp // code
tool_events_id.hpp // Events ID

Konsepnya adalah untuk mendefinisikan peristiwa tool_events_id.hppseperti itu:

// EVENT_NAME                         ID      BEGIN_END BG_COLOR NAME
#define SOCK_PDU_RECV_D               0x0301  //@D00301 BGEEAAAA # TX_PDU_Recv
#define SOCK_PDU_RECV_F               0x0302  //@F00301 BGEEAAAA # TX_PDU_Recv

Anda juga mendefinisikan beberapa fungsi di toolname.hpp:

#define LOG_LEVEL_ERROR 0
#define LOG_LEVEL_WARN 1
// ...

void init(void);
void probe(id,payload);
// etc

Di mana pun dalam kode Anda, Anda dapat menggunakan:

toolname<LOG_LEVEL>::log(EVENT_NAME,VALUE);

The probefungsi menggunakan beberapa lini perakitan untuk mengambil jam timestamp ASAP dan kemudian menetapkan entri dalam buffer. Kami juga memiliki peningkatan atom untuk menemukan indeks tempat penyimpanan log dengan aman. Tentu saja buffer berbentuk lingkaran.

Semoga idenya tidak dikaburkan oleh kurangnya kode sampel.


1

Sebenarnya sedikit terkejut tidak banyak disebutkan tentang google / benchmark , sementara itu agak rumit untuk menyematkan area kode tertentu, khususnya jika basis kode sedikit besar, namun saya menemukan ini sangat membantu ketika digunakan dalam kombinasi dengancallgrind

IMHO mengidentifikasi bagian yang menyebabkan kemacetan adalah kuncinya di sini. Namun saya akan mencoba dan menjawab pertanyaan-pertanyaan berikut terlebih dahulu dan memilih alat berdasarkan itu

  1. apakah algoritme saya benar?
  2. apakah ada kunci yang terbukti sebagai leher botol?
  3. apakah ada bagian kode tertentu yang terbukti menjadi pelakunya?
  4. bagaimana dengan IO, ditangani dan dioptimalkan?

valgrinddengan kombinasi callrinddan kcachegrindharus memberikan estimasi yang layak pada poin-poin di atas dan setelah itu ditetapkan bahwa ada masalah dengan beberapa bagian kode, saya sarankan melakukan micro bench mark google benchmarkadalah tempat yang baik untuk memulai.


1

Gunakan -pgbendera saat mengkompilasi dan menautkan kode dan menjalankan file yang dapat dieksekusi. Saat program ini dijalankan, profiling data dikumpulkan dalam file a.out.
Ada dua jenis profil yang berbeda

1- Profil datar:
dengan menjalankan perintah, gprog --flat-profile a.outAnda mendapatkan data berikut
- berapa persen dari keseluruhan waktu yang dihabiskan untuk fungsi,
- berapa detik yang dihabiskan dalam suatu fungsi — termasuk dan tidak termasuk panggilan ke subfungsi,
- jumlah panggilan,
- waktu rata-rata per panggilan.

2 - grafik profiling
kita perintah gprof --graph a.outuntuk mendapatkan data berikut untuk setiap fungsi yang meliputi
- Di setiap bagian, satu fungsi ditandai dengan nomor indeks.
- Di atas fungsi, ada daftar fungsi yang memanggil fungsi.
- Fungsi di bawah ini, ada daftar fungsi yang dipanggil oleh fungsi.

Untuk mendapatkan info lebih lanjut, Anda dapat melihat di https://sourceware.org/binutils/docs-2.32/gprof/


0

Karena tidak ada yang menyebutkan Arm MAP, saya akan menambahkan secara pribadi saya telah berhasil menggunakan Peta ke profil program ilmiah C ++.

Arm MAP adalah profiler untuk kode C, C ++, Fortran, dan F90 yang paralel, multithreaded, atau tunggal. Ini memberikan analisis mendalam dan menentukan titik kemacetan ke baris sumber. Tidak seperti kebanyakan profiler, ini dirancang untuk dapat profil pthreads, OpenMP atau MPI untuk kode paralel dan berulir.

MAP adalah perangkat lunak komersial.


0

menggunakan perangkat lunak debug bagaimana mengidentifikasi di mana kode berjalan lambat?

hanya berpikir Anda memiliki hambatan saat Anda bergerak maka itu akan menurunkan kecepatan Anda

seperti itu pengulangan realokasi yang tidak diinginkan, buffer overflows, pencarian, kebocoran memori dll operasi mengkonsumsi lebih banyak daya eksekusi itu akan berpengaruh buruk terhadap kinerja kode, Pastikan untuk menambahkan -pg ke kompilasi sebelum membuat profil:

g++ your_prg.cpp -pgatau cc my_program.cpp -g -pgsesuai kompiler Anda

belum mencobanya tetapi saya pernah mendengar hal-hal baik tentang google-perftools. Ini pasti patut dicoba.

valgrind --tool=callgrind ./(Your binary)

Ini akan menghasilkan file bernama gmon.out atau callgrind.out.x. Anda kemudian dapat menggunakan alat kcachegrind atau debugger untuk membaca file ini. Ini akan memberi Anda analisis grafis dari hal-hal dengan hasil seperti garis mana yang menghabiskan biaya.

aku pikir begitu

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.