Pertanyaan Bagaimana saya bisa membuat profil kode C ++ di Linux?


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


1498
2017-12-17 20:29


asal


Jawaban:


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

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

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

Anda mungkin memiliki beberapa masalah kinerja dengan berbagai ukuran. Jika Anda membersihkan salah satu dari mereka, yang tersisa akan mengambil persentase yang lebih besar, dan lebih mudah dikenali, pada pass berikutnya. Ini efek pembesaran, bila diperparah dengan banyak masalah, dapat mengarah ke faktor-faktor percepatan yang benar-benar besar.

Peringatan: Programmer 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 mengambil sampel seluruh panggilan stack, dan kemudian membiarkan Anda memeriksa satu set sampel acak. (Ringkasannya adalah tempat wawasan hilang.) Grafik panggilan tidak memberi Anda informasi yang sama, karena

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

Mereka juga akan mengatakan bahwa ini hanya bekerja pada program mainan, ketika benar-benar berfungsi 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 bahwa kadang-kadang menemukan hal-hal yang bukan masalah, tetapi itu hanya benar jika Anda melihat sesuatu sekali. Jika Anda melihat masalah pada lebih dari satu sampel, itu nyata.

P.S. Ini juga dapat dilakukan pada program multi-thread jika ada cara untuk mengumpulkan sampel tumpukan panggilan dari kolam thread pada suatu titik waktu, seperti yang ada di Jawa.

P.P.S Sebagai generalitas kasar, semakin banyak lapisan abstraksi yang Anda miliki dalam perangkat lunak Anda, semakin besar kemungkinan Anda menemukan bahwa itulah penyebab masalah kinerja (dan peluang untuk mendapatkan percepatan).

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

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

DITAMBAHKAN: Biarkan saya membuat penjelasan Bayesian tentang cara kerjanya. Misalkan ada beberapa instruksi I (panggilan atau sebaliknya) yang ada di panggilan menumpuk beberapa pecahan f waktu (dan dengan demikian biaya yang banyak). Untuk kesederhanaan, kira kita tidak tahu apa f adalah, tetapi menganggap 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 a-priori.

Maka misalkan kita mengambil hanya 2 sampel tumpukan, dan kita melihat instruksi I pada kedua sampel, pengamatan yang ditunjuk o=2/2. Ini memberi kita perkiraan baru dari frekuensi f dari 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)

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 itu f > = 0,5 adalah 92%, naik dari asumsi sebelumnya sebesar 60%.

Anggap asumsi sebelumnya berbeda. Misalkan kita mengasumsikan P (f = 0,1) adalah 0,991 (hampir pasti), dan semua kemungkinan lainnya hampir tidak mungkin (0,001). Dengan kata lain, kepastian kita sebelumnya adalah itu I murah. Lalu kita mendapatkan:

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)

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) adalah 26%, naik dari asumsi sebelumnya sebesar 0,6%. Jadi Bayes memungkinkan kami memperbarui perkiraan biaya kemungkinan kami I. Jika jumlah data kecil, itu tidak memberi tahu kami secara akurat berapa biayanya, hanya itu cukup besar untuk diperbaiki.

Namun cara lain untuk melihatnya disebut Aturan Suksesi. Jika Anda melempar koin 2 kali, dan muncul kepala dua kali, apa yang memberitahu Anda tentang kemungkinan bobot koin? Cara terhormat untuk menjawab adalah dengan mengatakan bahwa itu adalah distribusi Beta, dengan nilai rata-rata (jumlah klik + 1) / (jumlah percobaan + 2) = (2 + 1) / (2 + 2) = 75%.

(Kuncinya adalah yang kita lihat I lebih dari sekali. Jika kita hanya melihatnya sekali, itu tidak memberi tahu kita banyak kecuali itu f > 0.)

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


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

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

enter image description here

Pengukuran bersifat horizontal; ia memberi tahu Anda bagian mana dari rutinitas spesifik waktu. Sampling adalah vertikal. Jika ada cara untuk menghindari apa yang seluruh program lakukan pada saat itu, dan jika Anda melihatnya pada contoh kedua, Anda telah menemukan hambatan. Itulah yang membuat perbedaan - melihat seluruh alasan untuk waktu yang dihabiskan, bukan hanya berapa banyak.


1192
2018-04-21 04:09



Kamu dapat memakai Valgrind dengan opsi berikut

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

Ini akan menghasilkan file yang disebut callgrind.out.x. Anda dapat menggunakannya kcachegrind alat untuk membaca file ini. Ini akan memberi Anda analisis grafis tentang hal-hal dengan hasil seperti garis yang biaya berapa.


472
2017-12-17 20:34



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

Pastikan untuk menambahkan -pg ke kompilasi sebelum membuat profil:

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

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

Pertanyaan terkait sini.

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


296
2017-08-17 11:48



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

Ini datang dengan profil sampling klasik (halaman manual) serta yang luar biasa timechart!

Yang penting adalah alat ini bisa pembuatan profil sistem dan tidak hanya memproses profil - mereka dapat menunjukkan interaksi antara untaian, proses dan kernel dan membiarkan Anda memahami penjadwalan dan ketergantungan I / O antar proses.

Alt text


216
2018-05-22 21:44



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

(wikipedia) Valgrind pada dasarnya adalah virtual   mesin menggunakan just-in-time (JIT)   teknik kompilasi, termasuk   rekompilasi dinamis. Tidak ada apa-apa dari   program asli yang pernah dijalankan   langsung pada prosesor host.   Sebaliknya, Valgrind menterjemahkan terlebih dahulu   program menjadi bentuk sementara yang lebih sederhana   disebut Perwakilan Menengah   (IR), yang merupakan prosesor netral,   Formulir berbasis SSA. Setelah konversi,   alat (lihat di bawah) gratis untuk dilakukan   transformasi apa pun yang diinginkan   di IR, sebelum diterjemahkan Valgrind   IR kembali ke kode mesin dan memungkinkan   prosesor host menjalankannya.

Callgrind adalah profiler yang membangunnya. Manfaat utama adalah Anda tidak perlu menjalankan aplikasi Anda selama berjam-jam untuk mendapatkan hasil yang dapat diandalkan. Bahkan satu detik berjalan cukup untuk mendapatkan hasil yang kuat dan tepercaya, karena Callgrind adalah a non-probing profiler.

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


65
2018-06-30 19:30



Ini adalah jawaban untuk Jawaban Gprof Nazgob.

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

  1. Tidak berfungsi dengan benar pada kode multi-berulir, kecuali Anda menggunakan solusi

  2. Grafik panggilan menjadi bingung oleh pointer fungsi. Contoh: Saya memiliki fungsi yang disebut multithread () yang memungkinkan saya untuk melakukan multi-thread fungsi tertentu melalui array tertentu (keduanya dilewatkan sebagai argumen). Namun Gprof, melihat semua panggilan ke multithread () sebagai setara untuk keperluan menghitung waktu komputasi pada anak-anak. Karena beberapa fungsi yang saya berikan ke multithread () membutuhkan waktu lebih lama daripada yang lain, grafik panggilan saya sebagian besar tidak berguna. (Kepada mereka yang bertanya-tanya apakah threading adalah masalah di sini: tidak, multithread () dapat dipilih, dan lakukan dalam hal ini, jalankan semuanya secara berurutan di thread panggilan saja).

  3. Ia mengatakan sini bahwa "... angka angka panggilan berasal dengan menghitung, bukan sampling. Mereka benar-benar akurat ...". Namun saya menemukan grafik panggilan saya memberi saya 5345859132 + 784984078 sebagai statistik panggilan ke fungsi yang paling saya sebut, di mana nomor pertama seharusnya panggilan langsung, dan panggilan rekursif kedua (yang semuanya dari dirinya sendiri). Karena ini menyiratkan saya memiliki bug, saya masukkan penghitung lama (64-bit) ke dalam kode dan melakukan hal yang sama lagi. Hitungan saya: 5345859132 panggilan langsung, dan 78094395406 panggilan rekursif. Ada banyak digit di sana, jadi saya akan menunjukkan panggilan rekursif yang saya ukur adalah 78bn, dibandingkan 784m dari Gprof: faktor 100 berbeda. Kedua dijalankan adalah kode single threaded dan unoptimised, satu dikompilasi -g dan yang lain -pg.

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


49
2018-06-08 08:01



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

Jadi ini yang saya sarankan. Jalankan program pertama:

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

Sekarang ketika berhasil dan kami ingin memulai pembuatan profil, kami harus berjalan di jendela lain:

callgrind_control -i on

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

callgrind_control -k

Sekarang kita 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 header kolom "Mandiri", jika tidak menunjukkan bahwa "main ()" adalah tugas yang paling memakan waktu. "Diri" menunjukkan berapa banyak masing-masing fungsi itu sendiri mengambil waktu, tidak bersama dengan tanggungan.


47
2018-02-23 21:28



Gunakan Valgrind, callgrind dan kcachegrind: 

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

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

Gunakan gprof (add -pg): 

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

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

Gunakan google-perftools: 

Menggunakan sampling waktu, mengungkapkan I / O dan bottleneck CPU terungkap.

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

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


8
2018-03-17 12:20