Come utilizzare lo strumento di profilazione Gprof su Linux (tutorial)
Man mano che il codice diventa più grande, aumenta anche il numero di funzioni e le chiamate di funzioni nidificate. Quindi, per analizzare le prestazioni di un tale programma, diventa necessario capire quali funzioni richiedono tempo per essere eseguite. Allo stesso modo, l’analisi della singola funzione dovrebbe anche fornire una chiara distinzione tra il tempo impiegato dalla funzione genitore e le altre funzioni chiamate all’interno della funzione genitore.
Gli strumenti software utilizzati per questo tipo di analisi delle prestazioni sono comunemente noti come profiler. In questo tutorial, discuteremo di un profiler basato su Linux chiamato gprof.
Tutti gli esempi presentati in questo articolo sono testati su Ubuntu, bash 4.2.45 e gprof 2.23.2.
Configurazione e utilizzo di Gprof
Ecco alcuni passaggi necessari per scaricare e configurare un ambiente per gprof:
- Se non è già installato, scarica e installa gprof eseguendo “apt-get install binutils”.‘
- Per verificare se gprof è installato correttamente, esegui il comando gprof e dovrebbe dare un errore come “a.out: No such file or directory”.
- Supponendo che il compilatore usi gcc o cc, compila il tuo codice con l’opzione “-pg” in modo che l’eseguibile includa codice aggiuntivo per scopi di profilazione.
- Eseguire il programma in modo normale. Al termine del programma, verrà prodotto un file denominato “gmon.out” (dati del profilo) nella stessa directory da cui è stato eseguito il programma.
- Usa il profiler gprof per elaborare questi dati del profiler (gmon.out) e produrre analisi delle prestazioni leggibili dall’uomo e statistiche del programma.
Ecco un esempio del codice che vogliamo profilare:
#include<stdio.h>
void some_other_test(void)
{
printf("n Inside the function some_other_test() n");
int i = 0;
for(;i<=0XFFFF;i++);
}
void yet_another_test(void)
{
printf("n Inside the function yet_other_test() n");
int i = 0;
for(;i<=0XFFFFFFF;i++);
}
void another_test(void)
{
printf("n Inside the function another_test() n");
int i = 0;
for(;i<=0XFFF;i++);
yet_another_test();
}
void test(void)
{
printf("n Inside the function test() n");
int i = 0;
for(;i<=0XFFFFFF;i++);
another_test();
}
int main(void)
{
printf("n Inside the function main() n");
int i = 0;
for(;i<=0XFFFFF;i++);
test();
some_other_test();
return 0;
}
Si noti che il codice sopra è solo un esempio fittizio che contiene cicli for per simulare il consumo di tempo nell’elaborazione della funzione.
Una volta inserito il codice, il primo passo è compilare il codice. Ecco come puoi farlo:
$ gcc -Wall -pg profile.c -o profile
Quindi puoi vedere che il codice è stato salvato in un file chiamato “profile.c” e il nome del file di output è “profilo”.
Una volta prodotto l’eseguibile denominato “profile”, è stato eseguito come qualsiasi altro programma:
$ ./profile
Inside the function main()
Inside the function test()
Inside the function another_test()
Inside the function yet_other_test()
Inside the function some_other_test()
Quindi puoi vedere che il programma è stato eseguito correttamente. Ha anche prodotto un file chiamato “gmon.out” nella stessa directory.
$ ls gmon.out
gmon.out
Ora usa l’utilità gprof come segue:
$ gprof profile gmon.out > output
Quindi puoi vedere che il nome dell’eseguibile e gmon.out sono stati forniti come argomenti al comando gprof e l’output è stato reindirizzato a un file chiamato “output”. Questo file contiene tutte le analisi statistiche delle prestazioni del programma “profilo” in una forma leggibile dall’uomo.
Il contenuto del risultato è suddiviso in tipi di informazioni — Flat Profile e Call Chart.
Profilo piatto
Ecco l’analisi statistica in forma di profilo piatto:

Le statistiche mostrate sopra sono note come profilo piatto. Ecco la spiegazione (presa dal file di output) del significato di ciascuna colonna:
- % volta – La percentuale del tempo di esecuzione totale del programma utilizzato da questa funzione.
- Secondi cumulativi – Una somma corrente del numero di secondi rappresentati da questa funzione e quelli sopra elencati.
- Autosecondi – Il numero di secondi rappresentato solo da questa funzione. Questo è il tipo principale per questo elenco.
- chiamata – Quante volte è stata invocata questa funzione, se questa funzione è profilata, altrimenti vuoto.
- ms/chiamata automatica – Il numero medio di millisecondi trascorsi in questa funzione per chiamata, se questa funzione è profilata, altrimenti vuoto.
- Totale ms/chiamata – Il numero medio di millisecondi trascorsi in questa funzione e nel suo discendente per chiamata, se questa funzione è profilata, altrimenti vuoto.
- Nome – Nome della funzione. Questo è il tipo minore per questo elenco. L’indice mostra la posizione della funzione nell’elenco gprof. Se l’indice è tra parentesi, mostra dove apparirebbe nell’elenco gprof se stampato.
Chiama il grafico
Ecco l’analisi statistica sotto forma di grafico delle chiamate:

Questa tabella descrive l’albero delle chiamate del programma ed è stata ordinata in base al tempo totale trascorso in ciascuna funzione e nei relativi figli. Ogni voce in questa tabella è composta da diverse righe. La riga del numero di indice nel margine sinistro elenca la funzione corrente. Le righe sopra elencano le funzioni che hanno chiamato questa funzione e le righe sotto elencano le funzioni che ha chiamato.
Ecco la spiegazione (presa dal file di output) del significato di ciascuna colonna:
- Indice – Un numero univoco assegnato a ciascun elemento della tabella. I numeri di indice sono ordinati numericamente. Il numero di indice è stampato accanto a ciascun nome di funzione, facilitando la ricerca della posizione della funzione nella tabella.
- % volta – Questa è la percentuale del tempo “totale” trascorso in questa funzione e nei suoi figli. Si noti che a causa di diversi punti di vista, funzionalità escluse dalle opzioni, ecc., questi numeri NON si sommano fino al 100%.
- Se stesso – Questa è la quantità totale di tempo trascorso in questa funzione. Per i genitori di funzioni, questo è il periodo di tempo che è stato propagato direttamente dalla funzione a questo genitore. Mentre, per i figli di funzione, questo è il periodo di tempo che è stato propagato direttamente dal figlio alla funzione.
- RAGAZZI – Questa è la quantità totale di tempo propagata a questa funzione dai suoi figli. Per i padri della funzione, questo è il periodo di tempo che è stato propagato dai figli della funzione a questo genitore. Considerando che, per i figli della funzione, questa è la quantità di tempo che è stata propagata dai figli del bambino alla funzione.
- Chiamato – Questo è il numero di volte in cui è stata chiamata la funzione. Se la funzione è stata chiamata in modo ricorsivo, il numero include solo le chiamate non ricorsive ed è seguito da un “+” e dal numero di chiamate ricorsive. Per i genitori di funzioni, questo è il numero di volte che questo genitore ha chiamato la funzione `/’ il numero totale di volte che la funzione è stata chiamata. Le chiamate a funzioni ricorsive non sono incluse nel conteggio dopo `/’. Considerando che, per la funzione figli, questo è il numero di volte in cui la funzione ha chiamato questo figlio “/” il numero totale di volte in cui il figlio è stato chiamato. Le chiamate ricorsive figlio non sono elencate nel numero dopo “/”.
- Nome – Il nome della funzione corrente. Il numero di indice viene stampato dopo di esso. Se la funzione è un membro di un ciclo, il numero del ciclo viene stampato tra il nome della funzione e il numero di indice. Per i genitori di funzione, questo è il nome del genitore. Il numero di indice del genitore viene stampato dopo di esso. Se il genitore è un membro di un ciclo, il numero del ciclo viene stampato tra il nome e il numero di indice. Per i figli della funzione, questo è il nome del figlio. Il numero di indice del bambino viene stampato dopo di esso. Se il bambino fa parte di un ciclo, il numero del ciclo viene stampato tra il nome e il numero di indice.
Tutta la spiegazione di cui sopra è presente anche nel file di output generato da gprof. Ne ho parlato qui per comodità dei lettori.
Nota: tutte queste spiegazioni vengono prodotte nel file di output ogni volta che viene eseguito gprof. Quindi, una volta compresi i dettagli, puoi utilizzare l’opzione della riga di comando gprof -b. Per leggere altre opzioni della riga di comando, leggi pagina di manuale di gprof.