Linux'ta çalışan C ++ kodunu nasıl profilleştirebilirim?


1816

Optimizasyon sürecinde olduğum Linux üzerinde çalışan bir C ++ uygulamam var. Kodumun hangi alanlarının yavaş çalıştığını nasıl belirleyebilirim?


27
Geliştirme yığınınız hakkında daha fazla veri sağlayacaksanız daha iyi yanıtlar alabilirsiniz. Intel ve Sun'dan profilciler var, ancak derleyicilerini kullanmanız gerekiyor. Bu bir seçenek mi?
Nazgob

2
Aşağıdaki bağlantıda zaten yanıtlanmıştır: stackoverflow.com/questions/2497211/…
Kapil Gupta

4
Cevapların çoğu codeprofiler. Ancak, önceliklerin ters çevrilmesi, önbellek örtüşmesi, kaynak çekişmesi vb. Optimizasyon ve performans faktörleri olabilir. İnsanların yavaş koduma bilgi okuduğunu düşünüyorum . SSS bu konuya atıfta bulunuyor.
Mart'ta sanatsız gürültü


3
Rastgele pstack kullanıyordum, çoğu zaman programın çoğu zaman olduğu en tipik yığını yazdıracak, böylece darboğaz işaret edecek.
Jose Manuel Gomez Alvarez

Yanıtlar:


1406

Hedefiniz bir profil oluşturucu kullanmaksa, önerilenlerden birini kullanın.

Bununla birlikte, aceleniz varsa ve öznel olarak yavaşken programınızı hata ayıklayıcı altında manuel olarak kesebilirsiniz, performans sorunlarını bulmanın basit bir yolu vardır.

Sadece birkaç kez durdurun ve her seferinde çağrı yığınına bakın. Zamanın bir yüzdesini,% 20 veya% 50'sini ya da her neyse israf eden bir kod varsa, bu, her örnek üzerindeki eylemde onu yakalama olasılığınızdır. Yani, kabaca üzerinde göreceğiniz örneklerin yüzdesi. Eğitimli bir tahminde bulunmaya gerek yoktur. Sorunun ne olduğu hakkında bir tahmininiz varsa, bu sorunu kanıtlayacak veya çürütecektir.

Farklı boyutlarda birden fazla performans sorununuz olabilir. Bunlardan herhangi birini temizlerseniz, kalanlar daha büyük bir yüzde alır ve sonraki geçişlerde daha kolay fark edilir. Bu büyütme etkisi , çoklu problemler üzerinde birleştiğinde, gerçekten büyük hızlanma faktörlerine yol açabilir.

Dikkat : Programcılar, kendileri kullanmadıkça bu tekniğe şüpheyle bakma eğilimindedir. Profilcilerin size bu bilgileri verdiğini söyleyeceklerdir, ancak bu yalnızca tüm çağrı yığınını örneklediklerinde doğrudur ve daha sonra rastgele bir örnek kümesini incelemenize izin verir. (Özet, içgörünün kaybolduğu yerdir.) Arama grafikleri size aynı bilgileri vermez, çünkü

  1. Talimat düzeyinde özetlemiyorlar ve
  2. Özyineleme varlığında kafa karıştırıcı özetler verirler.

Ayrıca, sadece herhangi bir programda çalıştığında sadece oyuncak programlarında çalıştığını ve daha büyük programlarda daha iyi çalıştığı görülüyor, çünkü daha fazla sorun yaşamaya eğilimliler. Onlar bazen sorunlar olmayan şeyleri bulur diyecekler, ama bir şey görürseniz bu sadece doğrudur kez . Birden fazla örnekte bir sorun görürseniz, bu gerçek.

Not Bu, Java'da olduğu gibi, iş parçacığı havuzunun çağrı yığını örneklerini belirli bir zamanda toplamanın bir yolu varsa, çok iş parçacıklı programlarda da yapılabilir.

PPS Kaba bir genel olarak, yazılımınızda soyutlama katmanları arttıkça, performans sorunlarının (ve hızlanma fırsatının) nedeni olduğunu bulma olasılığınız o kadar artar.

Eklendi : Açık olmayabilir, ancak yığın örnekleme tekniği özyineleme varlığında eşit derecede iyi çalışır. Bunun nedeni, bir talimatın kaldırılmasıyla kazanılacak sürenin, bir örnek içinde kaç kez meydana gelebileceğinden bağımsız olarak, onu içeren örneklerin oranı ile yaklaşık olarak tahmin edilmesidir.

Sıklıkla duyduğum bir diğer itiraz: " Rastgele bir yerde duracak ve gerçek sorunu kaçıracak ". Bu, gerçek sorunun ne olduğuna dair önceden bir kavrama sahip olmaktan gelir. Performans sorunlarının kilit özelliklerinden biri de beklentileri karşılamamasıdır. Örnekleme size bir şeyin bir sorun olduğunu söyler ve ilk tepkiniz inançsızlıktır. Bu doğaldır, ancak gerçek bir sorun olup olmadığından emin olabilirsiniz, bunun tersi de geçerlidir.

Eklendi : Nasıl çalıştığına dair Bayesci bir açıklama yapmama izin verin. IÇağrı yığınında fzamanın bir kısmını içeren bazı talimatlar (çağrı veya başka türlü) olduğunu varsayalım (ve bu nedenle maliyeti çok fazla). Basitlik açısından, ne folduğunu bilmediğimizi varsayalım, ancak 0,1, 0,2, 0,3, ... 0,9, 1,0 olduğunu ve bu olasılıkların her birinin önceki olasılığının 0,1 olduğunu varsayalım, bu nedenle bu maliyetlerin tümü eşit derecede olasıdır Önsel.

Sonra sadece 2 yığın numune aldığımızı Ive her iki numune için de belirtilen gözlemi gördüğümüzü varsayalım o=2/2. Bu, bize göre, şu sıklığa filişkin yeni tahminler verir I:

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                

Son sütun, örneğin, f% 60'lık bir önceki varsayımdan itibaren = 0.5'in olma olasılığının % 92 olduğunu söylüyor .

Önceki varsayımların farklı olduğunu varsayalım. Varsayalım ki P(f=0.1).991 (neredeyse kesin) ve diğer tüm olasılıklar neredeyse imkansızdır (0.001). Başka bir deyişle, bizim önceliğimiz Ibunun ucuz olması. Sonra şunu elde ederiz:

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                

Şimdi P(f >= 0.5),% 0.6'lık önceki varsayımdan% 26 olduğunu söylüyor . Bu yüzden Bayes, olası maliyetini tahmin etmemizi sağlıyor I. Veri miktarı azsa, maliyetin ne olduğunu doğru bir şekilde söylemez, sadece düzeltmeye değecek kadar büyüktür.

Buna bakmanın başka bir yoluna Ardıllık Kuralı denir . Bir madeni parayı 2 kez çevirirseniz ve her iki kez de gelirse, bu size madalyonun olası ağırlığı hakkında ne söyler? Saygın cevap yolu bunun ortalama değeri olan bir Beta dağılımı olduğunu söylemektir (number of hits + 1) / (number of tries + 2) = (2+1)/(2+2) = 75%.

(Anahtar, bir Ikereden fazla görmemizdir . Yalnızca bir kez görürsek, bu f> 0 dışında fazla bir şey söylemez. )

Bu nedenle, çok az sayıda örnek bile, gördüğü talimatların maliyeti hakkında bize çok şey söyleyebilir. (Ve Eğer., Maliyet ile orantılı, ortalama olarak, bir frekans ile görür nörnekleri alınır ve fdaha sonra, maliyet Igörünür nf+/-sqrt(nf(1-f))örnekler. Örnek, n=10, f=0.3, yani 3+/-1.4örnekler).


Eklendi : Ölçüm ve rastgele yığın örnekleme arasındaki fark için sezgisel bir his vermek için:
Yığını duvar saati zamanında bile örnekleyen profilciler var, ancak ortaya çıkan ölçümler (veya sıcak yol veya sıcak nokta) bir "darboğaz" kolayca gizleyebilirsiniz). Size göstermedikleri (ve kolayca yapabildikleri) gerçek örneklerin kendisidir. Hedefiniz için Ve eğer bulmak darboğaz, görmek gerekmez onlardan sayısı ise ortalama , 2 Tek gereken zamanın fraksiyonu bölünmesiyle elde edilir. Bu nedenle,% 30 zaman alırsa, ortalama 2 / .3 = 6.7 örnek bunu gösterecektir ve 20 örneğin bunu gösterme şansı% 99.2'dir.

Ölçümlerin incelenmesi ve yığın örneklerinin incelenmesi arasındaki farkın kelepçesiz bir çizimi. Darboğaz böyle büyük bir damla veya çok sayıda küçük kabarcık olabilir, hiçbir fark yaratmaz.

resim açıklamasını buraya girin

Ölçüm yataydır; size belirli rutinlerin ne kadar zaman aldığını söyler. Örnekleme dikeydir. Tüm programın o anda ne yaptığından kaçınmanın bir yolu varsa ve ikinci bir örnekte görürseniz, darboğazı buldunuz. Farkı yaratan şey budur - harcanan zamanın tamamını görmek, sadece ne kadar değil.


292
Bu temelde fakir bir adamın örnekleme profilcisi, bu harika, ama muhtemelen tamamen sahte sonuçlar verecek çok küçük bir örnekleme riski taşıyorsunuz.
Çarpışmalar

100
@Crash: "Zavallı adam" bölümünü tartışmayacağım :-) İstatistiksel ölçüm hassasiyetinin çok sayıda örnek gerektirdiği doğrudur, ancak iki çelişkili hedef vardır - ölçüm ve problem yeri. Ölçüm hassasiyetine değil, konum hassasiyetine ihtiyacınız olan ikincisine odaklanıyorum. Yani, örneğin, orta-yığın, tek bir fonksiyon çağrısı A () olabilir; % 50 oranındadır, ancak A () 'ya yapılan diğer birçok çağrı ile birlikte pahalı olmayan başka bir büyük B fonksiyonunda olabilir. İşlev sürelerinin kesin özetleri bir ipucu olabilir, ancak diğer her yığın örneği sorunu tespit edecektir.
Mike Dunlavey

41
... dünya, çağrı sayıları ve / veya ortalama zamanlama ile açıklamalı bir çağrı grafiğinin yeterince iyi olduğunu düşünüyor. O değil. Ve üzücü kısmı, çağrı yığınını örnekleyenler için, en yararlı bilgiler tam önlerinde olmakla birlikte, "istatistik" çıkarları için onları atıyorlar.
Mike Dunlavey

30
Tekniğinize katılmıyorum demek istemiyorum. Açıkçası yığın yürüyen örnekleme profilerlerine oldukça fazla güveniyorum. Sadece bunu otomatik bir şekilde yapan bazı araçların olduğuna dikkat çekiyorum,% 25'ten% 15'e bir işlev elde etme noktasını geçtiğinizde ve% 1,2'den düşürmek gerektiğinde önemlidir. % 0.6.
Crashworks

13
-1: Düzgün bir fikir, ancak orta düzeyde performans odaklı bir ortamda bile çalışmak için para alırsanız, bu herkesin zamanının kaybıdır. Gerçek bir profiller kullanın, böylece arkanıza gelip gerçek sorunları düzeltmemize gerek kalmaz.
Sam Harwell

583

Sen kullanabilirsiniz Valgrind aşağıdaki seçeneklerle

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

Adlı bir dosya oluşturur callgrind.out.x. Daha sonra kcachegrindbu dosyayı okumak için aracı kullanabilirsiniz . Size, hangi satırların maliyete mal olduğu gibi sonuçların olduğu şeylerin grafiksel analizini verecektir.


51
valgrind harika, ama programınızı yavaşlatacak uyarılırsınız
neves

30
Çıktıyı görselleştirmenin inanılmaz bir alternatif yolu için Gprof2Dot'a da göz atın . ./gprof2dot.py -f callgrind callgrind.out.x | dot -Tsvg -o output.svg
Sebastian

2
@ neves Evet Valgrind, "gstreamer" ve "opencv" uygulamalarının gerçek zamanlı olarak profillenmesi açısından hız açısından çok yararlı değildir.
enthusiasticgeek

1
stackoverflow.com/questions/375913/… hız sorunu için kısmi bir çözümdür.
Tõnu Samuel

3
@Sebastian: gprof2dotşimdi burada: github.com/jrfonseca/gprof2dot
John Zwinck

348

GCC kullandığınızı varsayıyorum. Standart çözüm gprof ile profil oluşturmak olacaktır .

-pgProfil oluşturmadan önce derlemeye eklediğinizden emin olun :

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

Henüz denemedim ama google-perftools hakkında iyi şeyler duydum . Kesinlikle denemeye değer.

İlgili soru burada .

gprofİşinizi sizin için yapmazsa birkaç başka buzzwords : Valgrind , Intel VTune , Sun DTrace .


3
Gprof'un mevcut standart olduğunu kabul ediyorum. Bununla birlikte, Valgrind, hız optimizasyonu için değil, programlarınızdaki bellek sızıntılarını ve diğer bellekle ilgili yönlerini profilini belirlemek için kullanılır.
Bill the Lizard

68
Bill, vaglrind süitinde callgrind ve massif bulabilirsiniz. Her ikisi de profil uygulamaları için oldukça kullanışlıdır
Dario Minonne

7
@ Fatura-Lizard: bazı yorumlar gprof'ın : stackoverflow.com/questions/1777556/alternatives-to-gprof/...
Mike Dunlavey

6
gprof -pg yalnızca çağrı çağrı profili oluşturmaya yaklaşık bir yaklaşımdır. Hangi işlevlerin hangi işlevleri çağırdığını izlemek için mcount çağrıları ekler. Zaman için standart zamana dayalı örnekleme kullanır. Daha sonra, foo () işlevine çağrıların sayısı ile orantılı olarak foo () işlevinde örneklenen zamanları ekler. Bu yüzden farklı maliyetlerin çağrıları arasında ayrım yapmaz.
Krazy Glew

1
Clang / clang ++ ile, gperftools'un CPU profil oluşturucusunu kullanmayı düşünebilirsiniz . Dikkat: Bunu ben yapmadım.
einpoklum

257

Daha yeni çekirdekler (örneğin en son Ubuntu çekirdekleri) yeni 'perf' araçları ( apt-get install linux-tools) AKA perf_events ile birlikte gelir .

Bunlar klasik örnekleme profilleri ( man-page ) ve harika zaman çizelgesi ile geliyor !

Önemli olan, bu araçların sistem profili oluşturma ve yalnızca işlem profili oluşturma değil, iş parçacıkları, işlemler ve çekirdek arasındaki etkileşimi göstermeleri ve işlemler arasındaki zamanlama ve G / Ç bağımlılıklarını anlamanıza olanak vermesidir.

Alternatif metin


12
Harika bir araç! Benim için "main-> func1-> fun2" tarzından başlayan tipik bir "kelebek" görünümü elde etmek için yine de var mı? Bunu perf report
anlayamıyorum

Will, iş parçacığı etkinliğinin zaman çizelgesini gösterebilir; CPU numarası bilgisi eklendi mi? Her CPU'da ne zaman ve hangi iş parçacığının çalıştığını görmek istiyorum.
osgx

2
@ kizzx2 - gprof2dotve kullanabilirsiniz perf script. Çok güzel bir araç!
tire

2
4.13 gibi daha yeni çekirdeklerde bile profil oluşturma için eBPF bulunur. Bkz. Brendangregg.com/blog/2015-05-15/ebpf-one-small-step.html ve brendangregg.com/ebpf.html
Andrew Stern

Bir başka güzel tanıtım perfbulunmuyorsa archive.li/9r927#selection-767.126-767.271 (SO tanrılar SO bilgi bankasından o sayfayı silmeye karar Neden .... beni aşıyor)
ragerdl

75

Profilleme takım takımım için bir üs olarak Valgrind ve Callgrind kullanırdım. Bilmeniz gereken önemli olan Valgrind'in temelde bir Sanal Makine olmasıdır:

(wikipedia) Valgrind aslında dinamik yeniden derleme de dahil olmak üzere tam zamanında (JIT) derleme tekniklerini kullanan bir sanal makinedir. Orijinal programdan hiçbir şey doğrudan ana bilgisayar işlemcisi üzerinde çalıştırılmaz. Bunun yerine, Valgrind ilk önce programı işlemci-nötr, SSA tabanlı bir form olan Ara Temsil (IR) adı verilen geçici, daha basit bir forma dönüştürür. Dönüştürmeden sonra, bir araç (aşağıya bakın), Valgrind IR'yi tekrar makine koduna dönüştürmeden ve ana bilgisayar işlemcisinin çalıştırmasına izin vermeden önce, IR'de istediği dönüştürmeleri yapmakta serbesttir.

Callgrind bunun üzerine kurulmuş bir profil oluşturucu. Temel faydası, güvenilir sonuç almak için uygulamanızı saatlerce çalıştırmak zorunda kalmamanızdır. Callgrind bir nedeni bile bir saniye koşmak, kaya gibi sağlam, güvenilir sonuçlar elde etmek için yeterlidir olmayan sondalama profilci.

Valgrind üzerine kurulmuş bir diğer araç Massif. Ben yığın bellek kullanımı profil için kullanın. Harika çalışıyor. Yaptığı şey, bellek kullanımının anlık görüntülerini vermesidir - ayrıntılı bilgi Belleğin yüzde kaçını tutar ve WHO bunu oraya koymuştur. Bu tür bilgiler, uygulama çalıştırmanın farklı zaman noktalarında mevcuttur.


70

valgrind --tool=callgrindBazı seçenekler olmadan çalışmanın cevabı tam değil. Valgrind altında genellikle 10 dakikalık yavaş başlatma zamanını profillemek istemiyoruz ve bir görev yaparken programımızı profille paylaşmak istiyoruz.

Bu yüzden tavsiye ederim. Önce programı çalıştır:

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

Şimdi işe yaradığında ve profil oluşturmaya başlamak istediğimizde başka bir pencerede çalışmalıyız:

callgrind_control -i on

Bu profil oluşturmayı açar. Kapatmak ve tüm görevi durdurmak için şunları kullanabiliriz:

callgrind_control -k

Şimdi geçerli dizinde callgrind.out. * Adında bazı dosyalarımız var. Profil oluşturma sonuçlarını görmek için şunu kullanın:

kcachegrind callgrind.out.*

Bir sonraki pencerede "Öz" sütun başlığını tıklatmanızı öneririm, aksi takdirde "main ()" en çok zaman alan bir iştir. "Benlik", bağımlılarla birlikte değil, her işlevin kendisinin ne kadar zaman aldığını gösterir.


9
Şimdi nedense callgrind.out. * Dosyaları her zaman boştu. Callgrind_control -d komutunun yürütülmesi, verilerin diske dökülmesini zorlamak için kullanışlıdır.
Tõnu Samuel

3
Yapamam. Her zamanki bağlamlarım bütün MySQL veya PHP veya benzeri büyük bir şey gibi. Hatta ilk başta ne ayırmak istediğimi bile bilmiyorum.
Tõnu Samuel

2
Ya da benim durumumda programım aslında bir sürü veri LRU önbelleğine yükler ve ben bunu profil istiyorum. Bu yüzden başlangıçta önbelleğin bir alt kümesini zorla yüklüyorum ve yalnızca bu verileri kullanarak kodu profili oluşturuyorum (OS + CPU'nun önbelleğimdeki bellek kullanımını yönetmesine izin vermek). Çalışıyor, ancak bu önbelleği yüklemek farklı bir bağlamda profil oluşturmaya çalıştığım kod boyunca yavaş ve CPU yoğun, bu yüzden callgrind kötü kirlenmiş sonuçlar üretir.
Kod Abominator

2
CALLGRIND_TOGGLE_COLLECTtoplama programlı olarak etkinleştirmek / devre dışı bırakmak için de vardır ; bkz. stackoverflow.com/a/13700817/288875
Andre Holzner

1
Vay canına, bunun var olduğunu bilmiyordum, teşekkürler!
Vincent Fourmond

59

Bu Nazgob'un Gprof cevabına bir cevaptır .

Son birkaç gündür Gprof kullanıyorum ve üç önemli sınırlama buldum, bunlardan biri başka bir yerde belgelenmediğini gördüm (henüz):

  1. Geçici çözüm kullanmadığınız sürece, çok iş parçacıklı kodda düzgün çalışmaz

  2. Çağrı grafiği fonksiyon göstericileri tarafından karıştırılır. Örnek: multithread()Belirli bir dizi (her ikisi de bağımsız değişken olarak iletilen) üzerinde belirtilen bir işlevi çok iş parçacıklı sağlayan bir işlev var . Ancak Gprof, multithread()çocuklara harcanan zamanı hesaplamak amacıyla yapılan tüm çağrıları eşdeğer olarak görür. Bazı fonksiyonlar multithread()diğerlerinden daha uzun sürdüğüm için çağrı grafiklerim çoğunlukla işe yaramaz. (Burada iş parçacığı olup olmadığını merak edenler için: hayır, multithread()isteğe bağlı olabilir ve bu durumda yaptım, her şeyi sırayla sadece çağrı iş parçacığı üzerinde çalıştırın).

  3. Burada "... çağrı sayısı rakamları örnekleme değil, sayma yoluyla elde edilir. Tamamen doğrudur ..." der . Yine de, arama grafiğimi, 5345859132 + 784984078 numaralı telefona, ilk numaranın doğrudan çağrılar olduğu ve ikinci özyinelemeli çağrıların (hepsi kendi başına) olduğu çağrı çağrısı olarak görüyorum. Bu bir hata olduğunu ima beri, uzun (64-bit) sayaçları koda koymak ve aynı çalıştırmak tekrar yaptı. Sayımlarım: 5345859132 doğrudan ve 78094395406 özyinelemeli çağrılar. Orada çok sayıda basamak var, bu yüzden ölçtüğüm yinelemeli çağrıların 78bn olduğunu, Gprof'tan 784m'ye dikkat çekeceğim: 100 farklı faktör. Her iki çalışır tek dişli ve iyileştirilmemiş kodunu derlenmiş biri vardı -gve diğer -pg.

Bu, 64 bit Debian Lenny altında çalışan GNU Gprof (Debian için GNU Binutils) 2.18.0.20080103 idi.


Evet, örnekleme yapıyor, ancak çağrı sayısı için değil. İlginç bir şekilde, bağlantınızı takip etmek beni sonunda yazıma bağlandığım manuel sayfanın güncellenmiş bir sürümüne götürdü, yeni URL: sourceware.org/binutils/docs/gprof/… Bu, cevabımın (iii) kısmındaki alıntıyı tekrarlıyor, "Çok iş parçacıklı uygulamalarda veya çok iş parçacıklı kitaplıklarla bağlantı kuran tek iş parçacıklı uygulamalarda sayımlar yalnızca sayma işlevi iş parçacığı açısından güvenli olduğunda belirleyicidir. (Not: glibc'deki mcount sayma işlevinin iş parçacığı olmadığına dikkat edin -kasa)."
Rob_before_edits

Bunun (iii) 'deki sonucumu açıklayıp açıklamadığı benim için net değil. Kodum -lpthread -lm ile bağlantılıydı ve tek iş parçacıklı çalışsa bile hem "pthread_t * thr" hem de "pthread_mutex_t nextLock = PTHREAD_MUTEX_INITIALIZER" statik değişkeni olarak tanımlandı. Normalde "çok iş parçacıklı kütüphanelerle bağlantı" nın aslında bu kütüphaneleri kullanmak anlamına geldiğini ve bundan daha fazla olduğunu varsayıyorum, ama yanılmış olabilirim!
Rob_before_edits

23

Valgrind, callgrind ve kcachegrind kullanın:

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

callgrind.out.x oluşturur. Kcachegrind kullanarak okuyun.

Gprof (add -pg) kullanın:

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

(çoklu iş parçacıkları, işlev işaretçileri için çok iyi değil)

Google-perftools kullanın:

Zaman örneklemesi kullanır, G / Ç ve CPU darboğazları ortaya çıkar.

Intel VTune en iyisidir (eğitim amaçlı ücretsiz).

Diğerleri: AMD Codeanalyst (AMD CodeXL ile değiştirildiğinden beri), OProfile, 'perf' araçları (apt-get install linux-tools)


10

C ++ profil oluşturma tekniklerinin incelenmesi

Bu cevapta, bu araçların nasıl çalıştığını somut bir şekilde karşılaştırmak için birkaç çok basit test programını analiz etmek için birkaç farklı araç kullanacağım.

Aşağıdaki test programı çok basittir ve aşağıdakileri yapar:

  • mainaramalar fastve maybe_slow3 kez, bir maybe_slowyavaş olmak aramalar

    Yavaş çağrısı maybe_slow10 kat daha uzundur ve alt işleve çağrıları dikkate alırsak çalışma zamanına hakim olur common. İdeal olarak, profil oluşturma aracı bizi belirli yavaş aramaya yönlendirebilir.

  • her ikisi de fastve maybe_slowçağrı common, hangi program yürütme toplu hesap

  • Program arayüzü:

    ./main.out [n [seed]]

    ve program O(n^2)toplamda döngüler yapar . seedsadece çalışma zamanını etkilemeden farklı çıktılar elde etmektir.

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, yazılımın enstrümantasyon ile derlenmesini gerektirir ve ayrıca bu enstrümantasyon ile birlikte bir örnekleme yaklaşımı kullanır. Bu nedenle doğruluk (örnekleme her zaman tam olarak doğru değildir ve işlevleri atlayabilir) ve yürütme yavaşlaması (enstrümantasyon ve örnekleme, yürütmeyi çok yavaşlatmayan nispeten hızlı teknikler) arasında bir denge kurar.

gprof, GCC / binutils'e entegre edilmiştir, bu yüzden tek yapmamız gereken -pggprof'u etkinleştirme seçeneğiyle derlemektir . Programı normalde birkaç saniyelik makul bir süre üreten bir boyut CLI parametresiyle çalıştırırız ( 10000):

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

Eğitimsel nedenlerden dolayı, optimizasyonlar etkinleştirilmeden de bir çalışma yapacağız. Normalde yalnızca optimize edilmiş programın performansını optimize etmeyi önemsediğiniz için, bunun pratikte işe yaramaz olduğuna dikkat edin:

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

İlk olarak, timebize ve olmadan yürütme süresinin -pgaynı olduğunu söyler , bu harika: yavaşlama yok! Bununla birlikte, karmaşık yazılımda 2x - 3x yavaşlama hesaplarını gördüm, örneğin bu bilette gösterildiği gibi .

Derlediğimiz için -pg, programı çalıştırmak gmon.outprofil oluşturma verilerini içeren bir dosya dosyası üretir .

Bu dosyayı şu şekilde grafiksel gprof2dotolarak gözlemleyebiliriz : gprof sonuçlarının grafik temsili mümkün müdür?

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

Burada gprofaraç, gmon.outizleme bilgilerini okur ve içinde okunabilir bir rapor oluşturur main.gprof; bu rapor gprof2dotdaha sonra bir grafik oluşturmak için okunur.

Gprof2dot kaynağı şuradadır: https://github.com/jrfonseca/gprof2dot

-O0Koşu için aşağıdakileri gözlemliyoruz :

resim açıklamasını buraya girin

ve -O3koşu için:

resim açıklamasını buraya girin

-O0Çıkış hemen hemen kendi kendini açıklayıcı. Örneğin, 3 maybe_slowçağrının ve onların çocuk çağrısının toplam çalışma zamanının% 97,56'sını aldığını gösterir, ancak maybe_slowçocuksuz kendisinin yürütülmesi toplam yürütme süresinin% 0,00'ünü oluşturur , yani bu işlevde harcanan neredeyse tüm zaman çocuk çağrıları.

YAPILACAKLAR: GDB'de görebilmeme rağmen neden çıktıda maineksik ? GProf çıkışında eksik işlev, gprof'un derlenmiş enstrümantasyonuna ek olarak örnekleme temelli olması ve çok hızlı olması ve örnek almaması nedeniyle olduğunu düşünüyorum .-O3bt-O3 main

PNG yerine SVG çıktısını seçiyorum çünkü SVG Ctrl + F ile aranabilir ve dosya boyutu yaklaşık 10 kat daha küçük olabilir. Ayrıca, oluşturulan görüntünün genişliği ve yüksekliği, karmaşık yazılımlar için on binlerce pikselle eogkomik olabilir ve bu durumda PNG'ler için GNOME 3.28.1 hata verirken SVG'ler tarayıcım tarafından otomatik olarak açılır. gimp 2.8 iyi çalıştı, ayrıca bkz:

ancak o zaman bile, istediğinizi bulmak için görüntüyü çok fazla sürükleyeceksiniz, örneğin bu biletten alınan "gerçek" bir yazılım örneğinden bu resmi inceleyin :

resim açıklamasını buraya girin

Tüm bu küçük sıralanmamış spagetti hatları birbirinin üzerinden geçerek en kritik çağrı yığınını kolayca bulabilir misiniz? dotEminim daha iyi seçenekler olabilir , ama şimdi oraya gitmek istemiyorum. Gerçekten ihtiyacımız olan şey, bunun için uygun bir görüntüleyicidir, ancak henüz bir tane bulamadım:

Ancak bu sorunu biraz azaltmak için renk haritasını kullanabilirsiniz. Örneğin, önceki büyük görüntüde, yeşilin kırmızıdan sonra geldiği parlak kesinti, ardından daha koyu ve daha koyu maviyi çıkardığımda sonunda soldaki kritik yolu bulmayı başardım.

Alternatif olarak, gprofdaha önce kaydettiğimiz yerleşik binutils aracının metin çıktısını da gözlemleyebiliriz :

cat main.gprof

Varsayılan olarak, bu çıktı verilerinin ne anlama geldiğini açıklayan son derece ayrıntılı bir çıktı üretir. Bundan daha iyi açıklayamadığım için, kendiniz okumanıza izin vereceğim.

Veri çıkış formatını anladıktan sonra, -bseçenekle birlikte öğretici olmadan yalnızca verileri göstermek için ayrıntı düzeyini azaltabilirsiniz :

gprof -b main.out

Örneğimizde, çıktılar -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

ve için -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

Her bölüm için çok hızlı bir özet olarak, örneğin:

                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]

girintili bırakılan işlev ( maybe_flow) etrafında ortalanır . [3]bu işlevin kimliği. Fonksiyonun üstünde, arayanlar ve altında callees vardır.

Bunun için -O3, grafik çıktısında olduğu gibi maybe_slowve fastbilinen bir ebeveyne sahip olmadığına bakın, bu belgelerin ne <spontaneous>anlama geldiğini söylüyor .

Gprof ile satır satır profil oluşturmanın güzel bir yolu olup olmadığından emin değilim: belirli kod satırlarında harcanan `gprof` zamanı

valgrind callgrind

valgrind programı valgrind sanal makinesi üzerinden çalıştırır. Bu, profil oluşturmayı çok doğru hale getirir, ancak programın çok büyük bir yavaşlamasını da sağlar. Ben de daha önce kcachegrind bahsetmiştim: Resimli bir fonksiyon kodu kodunu almak için Araçlar

callgrind, valgrind'in profil kodu aracıdır ve kcachegrind, önbellek çıktısını görselleştirebilen bir KDE programıdır.

İlk önce -pgnormal derlemeye geri dönmek için bayrağı kaldırmalıyız , aksi takdirde çalışma aslında başarısız olurProfiling timer expired ve evet, bu çok yaygındır ve bunun için bir Stack Overflow sorusu vardı.

Bu yüzden derliyor ve şöyle çalışıyoruz:

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

Etkinleştiririm --dump-instr=yes --collect-jumps=yes çünkü bu aynı zamanda nispeten düşük ek maliyetle montaj başına performans dökümünü görmemizi sağlayan bilgileri döküyor.

Yarasadan, timeprogramın yürütülmesi için 29.5 saniye sürdüğünü söylüyor, bu nedenle bu örnekte yaklaşık 15x'lik bir yavaşlama yaşadık. Açıkçası, bu yavaşlama daha büyük iş yükleri için ciddi bir sınırlama olacaktır. Burada adı geçen "gerçek dünya yazılım örneği" nde 80x'lik bir yavaşlama gözlemledim.

Çalışma, callgrind.out.<pid>örneğin callgrind.out.8554benim durumumda adlı bir profil veri dosyası oluşturur . Bu dosyayı şu şekilde görüntülüyoruz:

kcachegrind callgrind.out.8554

Bu, metinsel gprof çıktısına benzer veriler içeren bir GUI'yi gösterir:

resim açıklamasını buraya girin

Ayrıca, sağ alt "Arama Grafiği" sekmesine gidersek, makul olmayan miktarda beyaz sınırla aşağıdaki resmi elde etmek için sağ tıklayarak dışa aktarabileceğimiz bir arama grafiği görürüz :-)

resim açıklamasını buraya girin

Bence fastbu grafikte gösterilmiyor çünkü kcachegrind görselleştirmeyi basitleştirmiş olmalı, çünkü bu çağrı çok az zaman alıyor, bu muhtemelen gerçek bir programda istediğiniz davranış olacak. Sağ tıklama menüsü, bu tür düğümleri ne zaman iptal edeceğinizi kontrol etmek için bazı ayarlara sahiptir, ancak hızlı bir denemeden sonra böyle kısa bir çağrı gösteremedim. fastSol pencereye tıklarsam, bir çağrı grafiği gösterilir fast, böylece yığın gerçekten yakalanır. Hiç kimse henüz tam grafik çağrı grafiğini göstermenin bir yolunu bulamamıştı : callgrind'in kcachegrind çağrı grafiğindeki tüm fonksiyon çağrılarını göstermesini sağlayın

Karmaşık C ++ yazılımında YAPILACAKLAR, bazı tür girişler görüyorum <cycle N>, örneğin <cycle 11>fonksiyon isimlerini beklediğim yerde, bu ne anlama geliyor? Bunu açıp kapatmak için bir "Döngü Algılama" düğmesi olduğunu fark ettim, ama bu ne anlama geliyor?

perf itibaren linux-tools

perfyalnızca Linux çekirdek örnekleme mekanizmalarını kullanıyor gibi görünüyor. Bu, kurulumu çok basit hale getirir, ancak tam olarak doğru değildir.

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

Bu, yürütmeye 0,2 saniye ekledi, bu yüzden zaman açısından iyiyiz, ancak commondüğümü klavyenin sağ okuyla genişlettikten sonra hala çok fazla ilgi görmüyorum :

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              

O zaman -O0programı bir şey gösterip göstermediğini görmek için karşılaştırmaya çalışıyorum ve sadece şimdi, bir arama grafiği görüyorum:

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              

YAPILACAKLAR: İnfazda ne oldu -O3? Basitçe bu mu maybe_slowve fastçok hızlıydı ve herhangi bir örnek almadı mı? Yürütülmesi -O3daha uzun süren daha büyük programlarda iyi çalışır mı ? Bazı CLI seçeneklerini kaçırdım mı? -FHertz'de örnek frekansını kontrol etmeyi öğrendim , ancak varsayılan olarak izin verilen maksimum değere getirdim -F 39500(artırılabilir sudo) ve hala net çağrılar göremiyorum.

Hakkında harika bir şey perf, büyük çağrıları hızlı bir şekilde görmenizi sağlayan çağrı yığını zamanlamalarını çok düzgün bir şekilde görüntüleyen Brendan Gregg'in FlameGraph aracıdır. Aracı mevcuttur: https://github.com/brendangregg/FlameGraph ve ayrıca onun perf öğretici üzerinde söz edilir: http://www.brendangregg.com/perf.html#FlameGraphs koştum zaman perfolmadan sudoaldım ERROR: No stack counts foundbu yüzden için şimdi bunu yapacağım 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

ancak bu kadar basit bir programda, çıktıyı anlamak çok kolay değildir, çünkü ne grafikte ne maybe_slowde fastgrafikte kolayca göremeyiz :

resim açıklamasını buraya girin

Daha karmaşık bir örnekte, grafiğin ne anlama geldiği açıktır:

resim açıklamasını buraya girin

YAPILACAKLAR [unknown]Bu örnekte bir fonksiyon günlüğü var , neden?

Buna değer olabilecek başka bir GUI arayüzleri şunları içerir:

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

    Ancak bu, önce verileri Ortak İz Biçimi'ne dönüştürmeniz gereken dezavantaja sahiptir perf data --to-ctf, ancak derleme sırasında etkinleştirilmesi gerekir / perfyeterince yeni olması gerekir ; Ubuntu 18.04

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

    Bunun dezavantajı, Ubuntu paketi yok gibi görünüyor ve Ubuntu 18.04 Qt 5.9'da iken, Qt 5.10 gerektiriyor.

gperftools

Daha önce "Google Performans Araçları" olarak adlandırılan kaynak: https://github.com/gperftools/gperftools Örnek tabanlı.

İlk önce gperftools'u aşağıdakilerle kurun:

sudo apt install google-perftools

Ardından, gperftools CPU profil oluşturucuyu iki şekilde etkinleştirebiliriz: çalışma zamanında veya derleme zamanında.

Çalışma zamanında, örneğin sistemimde bulabileceğiniz LD_PRELOADnoktaya işaret etmeyi geçmeliyiz:libprofiler.solocate libprofiler.so

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

Alternatif olarak, kütüphaneyi bağlantı zamanında kurabiliriz LD_PRELOADve çalışma zamanında dağıtılır:

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

Ayrıca bakınız: gperftools - profil dosyası dökülmedi

Şimdiye kadar bulduğum bu verileri görüntülemenin en güzel yolu, pprof çıktısını kcachegrind'in girdi olarak aldığı biçim ile (evet, Valgrind-proje-görüntüleyici-aracı) yapmak ve kcachegrind'i kullanmaktır:

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

Bu yöntemlerden herhangi biriyle çalıştıktan sonra prof.outçıktı olarak bir profil veri dosyası alırız . Bu dosyayı grafiksel olarak bir SVG olarak görüntüleyebiliriz:

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

resim açıklamasını buraya girin

diğer araçlar gibi tanıdık bir çağrı grafiği verir, ancak saniyeden ziyade örneklerin saydam birimi ile.

Alternatif olarak, aşağıdakilerle bazı metin verileri de alabiliriz:

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

hangi verir:

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

Ayrıca bkz: Google perf araçlarını kullanma

Ubuntu 18.04, gprof2dot 2019.11.30, valgrind 3.13.0, perf 4.15.18, Linux çekirdeği 4.15.0, FLameGraph 1a0dc6985aad06e76857cf2a354bd5ba0c9ce96b, gperftools 2.5-2'de test edilmiştir.


2
Varsayılan olarak perf kaydı çerçeve işaretçi kaydını kullanır. Modern derleyiciler çerçeve adresini kaydetmez ve bunun yerine kaydı genel bir amaç olarak kullanır. Alternatif, -fno-omit-frame-pointerbayrakla derlemek veya farklı bir alternatif kullanmaktır: senaryonuza --call-graph "dwarf"veya --call-graph "lbr"senaryonuza bağlı olarak kayıt yapın.
Jorge Bellon

5

Tek iş parçacıklı programlar için igprof , The Ignominous Profiler'ı kullanabilirsiniz: https://igprof.org/ .

Kümülatif veya her işlevde harcanan zaman veya bellek ile açıklamalı, göz atılabilir bir çağrı yığını ağacına sonuçları sarın hediye olacak ... uzun ... cevabının çizgileri boyunca bir örnekleme profileridir. başına işlevi.


İlginç görünüyor, ancak GCC 9.2 ile derlenemiyor. (Debian / Sid) Github ile ilgili bir sorun çıkardım.
Basile Starynkevitch

5

Ayrıca bahsetmeye değer

  1. HPCToolkit ( http://hpctoolkit.org/ ) - Açık kaynak, paralel programlar için çalışır ve sonuçlara birden çok yolla bakmak için bir GUI'ye sahiptir
  2. Intel VTune ( https://software.intel.com/en-us/vtune ) - Intel derleyicileriniz varsa bu çok iyi
  3. TAU ( http://www.cs.uoregon.edu/research/tau/home.php )

HPCToolkit ve VTune kullandım ve çadırda uzun kutbu bulma konusunda çok etkilidirler ve kodunuzun yeniden derlenmesine gerek yoktur (anlamlı çıktı elde etmek için CMake'de -g -O veya RelWithDebInfo türü derleme kullanmanız gerekir) . TAU'nun yeteneklerde benzer olduğunu duydum.


4

Kodumu hızlandırmak için kullandığım iki yöntem bunlar:

CPU bağlantılı uygulamalar için:

  1. Kodunuzun şüpheli kısımlarını belirlemek için DEBUG modunda bir profil oluşturucu kullanın
  2. Ardından RELEASE moduna geçin ve performansta değişiklikler görene kadar kodunuzun şüpheli bölümlerini (hiçbir şey olmadan saplama) yorumlayın.

G / Ç bağlantılı uygulamalar için:

  1. Kodunuzun şüpheli kısımlarını tanımlamak için RELEASE modunda bir profil oluşturucu kullanın.

NB

Profiliniz yoksa fakir adamın profilini kullanın. Uygulamanızda hata ayıklarken duraklat tuşuna basın. Çoğu geliştirici paketi yorum satır numaralarıyla bir araya gelir. İstatistiksel olarak, CPU döngülerinizin çoğunu yiyen bir bölgeye inmeniz muhtemeldir.

CPU için, DEBUG modunda profil oluşturmanın nedeni , RELEASE modunda profil oluşturmayı denediyseniz , derleyici matematiği azaltacak, döngüleri vektörleştirecek ve kodunuzu bir araya getirildiğinde eşlenemez bir karmaşaya sürükleyecek satır içi işlevleri kullanacaktır. Eşlenemeyen bir karmaşa, profil oluşturucunuzun neyin uzun sürdüğünü net bir şekilde tanımlayamayacağı anlamına gelir, çünkü montaj optimizasyon altındaki kaynak koduna karşılık gelmeyebilir . RELEASE modunun performansına (örn. Zamanlamaya duyarlı) ihtiyacınız varsa , kullanılabilir bir performans sağlamak için hata ayıklayıcı özelliklerini gerektiği gibi devre dışı bırakın .

G / Ç bağlantısı için, profil oluşturucu yine de ÇIKIŞ modunda G / Ç işlemlerini tanımlayabilir, çünkü G / Ç işlemleri ya paylaşılan bir kitaplığa harici olarak bağlıdır (çoğu zaman) ya da en kötü durumda bir sistemle sonuçlanır. kesme vektörü (profiler tarafından da kolayca tanımlanabilir).


2
+1 Zavallı adamın yöntemi CPU bağlı olduğu gibi I / O bağlı için de işe yarar ve tüm performans ayarlarını DEBUG modunda yapmanızı öneririm. Ayarlamayı bitirdiğinizde, RELEASE'i açın. Program kodunuzda CPU'ya bağlıysa bir iyileştirme yapacaktır. İşte sürecin kaba ama kısa bir videosu.
Mike Dunlavey

3
Performans profili oluşturma için DEBUG yapılarını kullanmam. DEBUG modundaki performans açısından kritik parçaların serbest bırakma modunda tamamen optimize edildiğini gördüm. Başka bir sorun, performansa gürültü katan hata ayıklama kodunda eklerin kullanılmasıdır.
gast128

3
Yazımı hiç okudun mu? "RELEASE modunun performansına (örn. Zamanlamaya duyarlı) ihtiyacınız varsa, kullanılabilir bir performans sağlamak için hata ayıklayıcı özelliklerini devre dışı bırakın", "Sonra RELEASE moduna geçin ve kodunuzun şüpheli bölümlerini yorumlayın (hiçbir şey olmadan saplayın) performans değişiklikleri. "? Hata ayıklama modunda olası sorun alanlarını kontrol et ve bahsettiğiniz tuzaklardan kaçınmak için yayınlama modunda bu sorunları doğrula dedim.
seo


2

loguruProfilleme için güzelce kullanılabilen zaman damgaları ve toplam çalışma süresi içerdiği için bir günlükleme çerçevesi kullanabilirsiniz :


1

İş yerinde, zamanlama açısından ne istediğimizi izlememize yardımcı olan gerçekten güzel bir aracımız var. Bu defalarca faydalı oldu.

C ++ 'tadır ve ihtiyaçlarınıza göre özelleştirilmelidir. Ne yazık ki kodu paylaşamıyorum, sadece kavramları. volatileMortgage sonrası veya günlükleme sistemini durdurduktan sonra (ve bunu örneğin bir dosyaya döktüğünüzde) zaman damgaları ve olay kimliği içeren bir "büyük" arabellek kullanırsınız .

Tüm verilerle büyük bir arabellek alırsınız ve küçük bir arabirim verileri ayrıştırır ve osiloskopun renklerle ( .hppdosyada yapılandırılmış) yaptığı gibi adı (yukarı / aşağı + değer) olan olayları gösterir .

Oluşturduğunuz etkinlik miktarını yalnızca istediğiniz şeye odaklanacak şekilde özelleştirirsiniz. Saniyede kaydedilen olayların miktarına bağlı olarak istediğimiz CPU miktarını tüketirken, zamanlama sorunları için bize çok yardımcı oldu.

3 dosyaya ihtiyacınız var:

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

Kavram olayları şöyle tanımlamaktır tool_events_id.hpp:

// 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

Ayrıca birkaç işlevi de tanımlarsınız toolname.hpp:

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

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

Kodunuzda nerede kullanabilirsiniz:

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

probeFonksiyon kısa sürede saat damgası almak için birkaç montaj hatları kullanır ve sonra tampon bir girdi ayarlar. Ayrıca, log olayının saklanacağı bir dizini güvenli bir şekilde bulmak için atomik bir artış var. Tabii ki tampon daireseldir.

Umarım fikir, örnek kod eksikliğinden gizlenmez.


1

Aslında , özellikle kod tabanı biraz büyükse, kodun belirli alanını sabitlemek biraz hantalken, google / benchmark hakkında çok fazla söz edilmediğinden biraz şaşırttı , ancak bununla birlikte kullanıldığında bunu gerçekten yararlı buldumcallgrind

Tıkanıklığa neden olan parçayı tanımlayan IMHO burada anahtar. Ancak önce aşağıdaki soruları cevaplamaya çalışıyorum ve buna dayalı bir araç seçiyorum

  1. algoritmam doğru mu?
  2. şişe boynu olduğunu kanıtlayan kilitler var mı?
  3. suçlu olduğunu kanıtlayan belirli bir kod bölümü var mı?
  4. ele ve optimize IO hakkında nasıl?

valgrindkombinasyonu ile callrindve kcachegrindyukarıdaki noktalarda iyi bir tahmin sağlamalıdır ve kodun bir bölümü ile ilgili sorunlar olduğu tespit edildikten sonra, bir mikro tezgah işareti google benchmarkbaşlamak için iyi bir yer olduğunu öneririm .


1

-pgKodu derlerken ve bağlarken bayrak kullanın ve yürütülebilir dosyayı çalıştırın. Bu program yürütülürken, profil oluşturma verileri a.out dosyasında toplanır.
İki farklı profil oluşturma vardır

1- Düz profil oluşturma:
Komutu çalıştırarak gprog --flat-profile a.outaşağıdaki verileri elde edersiniz
- işlev için toplam sürenin yüzde
kaçı - bir işlevde kaç saniye harcandı - alt işlevlere çağrılar dahil ve hariç
-, aramalar
- arama başına ortalama süre.

2-grafik
bize gprof --graph a.outiçeren her fonksiyon için aşağıdaki verileri almak için komut profilleme
- Her bölümde, bir fonksiyon bir indeks numarası ile işaretlenir.
- Fonksiyonun üstünde, fonksiyonu çağıran fonksiyonların bir listesi vardır.
- Fonksiyonun altında, fonksiyon tarafından çağrılan fonksiyonların bir listesi vardır.

Daha fazla bilgi için https://sourceware.org/binutils/docs-2.32/gprof/ adresini ziyaret edebilirsiniz.


0

Hiç kimse Arm MAP'den bahsetmediğim gibi, kişisel olarak C + + bilimsel bir programı profillemek için Map'i kullandığım için eklerdim.

Arm MAP, paralel, çok iş parçacıklı veya tek iş parçacıklı C, C ++, Fortran ve F90 kodları için profil oluşturucudur. Kaynak hattına derinlemesine analiz ve darboğaz belirleme sağlar. Çoğu profilerden farklı olarak, paralel ve iş parçacıklı kodlar için pthreads, OpenMP veya MPI'yı profilleyebilecek şekilde tasarlanmıştır.

MAP ticari bir yazılımdır.


0

Bir hata ayıklama yazılımı kullanmak nasıl kod yavaş çalışıyor nerede tanımlamak için?

hareket halindeyken bir engeliniz olduğunu düşünün, o zaman hızınızı azaltacaktır

İstenmeyen yeniden tahsisin döngü, arabellek taşmaları, arama, bellek sızıntıları vb. işlemleri, kodun performansı üzerinde olumsuz etki yaratacak daha fazla yürütme gücü tüketir, Profil oluşturmadan önce derlemeye -pg eklediğinizden emin olun:

g++ your_prg.cpp -pgveya cc my_program.cpp -g -pgderleyicinize göre

henüz denemedim ama google-perftools hakkında iyi şeyler duydum. Kesinlikle denemeye değer.

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

Gmon.out veya callgrind.out.x adlı bir dosya oluşturur. Daha sonra bu dosyayı okumak için kcachegrind veya debugger aracını kullanabilirsiniz. Size, hangi satırların maliyete mal olduğu gibi sonuçların olduğu şeylerin grafiksel analizini verecektir.

sanırım

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.