Misurare il tempo di esecuzione di un programma in [C]

Giovanni Canella

12/11/2013

4873

Nella realizzazione di un applicativo, qualunque programmatore deve riuscire ad arrivare alla soluzione del problema inizialmente posto, tramite il minor numero di passaggi possibili, strizzando anche l'occhio alla velocità, che riveste un ruolo fondamentale. In questo articolo vediamo come misurare i tempi di esecuzione di qualsiasi programma, con una precisione altissima!

In questo modo potremo verificare quale metodo è più veloce rispetto ad un altro, regolandoci di conseguenza per trovare l'alternativa migliore e rendere più performante il nostro programma! Il concetto che c'è dietro è molto semplice: tramite le funzioni che vi spiegherò successivamente ottieniamo il tempo iniziale e una volta concluso il listato da analizzare, misuriamo il tempo finale; infine con una sottrazione otteniamo il tempo impiegato.

 

Le funzioni

In tutto ci sono 5 metodi principali, con cui possiamo ottenere il tempo attuale, attraverso varie scale di precisione. Passando da quello meno preciso: time(), (sull'ordine dei secondi), fino a quello più preciso clock_gettime() (nell'ordine dei nanosecondi! Ovvero 1 miliardesimo di secondo).

Naturalmente ognuno ha il suo specifico utilizzo, in quanto per lo scopo di questo articolo è ovvio che andiamo scegliere il più accurato, mentre se vogliamo per esempio ottenere una data di registrazione allora possiamo affidarci benissimo a time().

Ecco una tabella:

Funzione Inclusa in... Tipo di dato che utilizza Precisione
time()   time_t secondo
ftime()   struct timeb millisecondo
clock()   clock_t millisecondo
gettimeofday()   struct timeval microsecondo
clock_gettime()   struct timespec nanosecondi

La scelta va a nostra completa discrezione, anche se personalmente consiglio di utiizzare: 

  • gettimeofday(),
  • clock_gettime().

 

gettimeofday(struct timeval *tp, void *tzp)

Innanzitutto dobbiamo includere le relative librerie:

#include 
#include 
#include 
#include 

Successivamente come visto dalla precedente tabella dobbiamo passarli come parametro una struttura di tipo timeval, impostata nel seguente modo:

struct timeval {
	__time_t tv_sec;			/* Secondi.  */
	__suseconds_t tv_usec;	/* Microsecondi.  */
};

che sarà "riempita" con i dati da noi desiderati.

struct timeval tempo;

Inoltre dichiariamo anche le seguenti variabili utilizzate poi nell'esecuzione del programma

// Dichiarazione variabili
double 	tempoIniziale = 0, tempoFinale = 0;
int 	i, j, righe, colonne;

che altro non è che un semplice esempio in cui  proveremo a effettuare un ciclo su un'array multidimensionale (o matrice) riempito con numeri casuali e vedere quanto tempo impiega a stampare tutti gli elementi a video. Naturalmente il tempo verrà misurato dopo il riempimento di quest'ultima, altrimenti ci verranno mostrati valori maggiorati. Per quanto riguarda gli indici, ho fatto in modo di prenderli in input con un semplice scanf(), invece che definire due costanti, mentre per i valori ho utilizzato la funzione rand(), che come possiamo capire genera dei numeri casuali, in base al seed impostato da srand().

printf("Inserisci righe e colonne della matrice: n");
scanf("%d%d", &righe, &colonne);

int matrice[righe][colonne];

srand(time(NULL));

for(i = 0; i < righe; i++) {
	for(j = 0; j < colonne; j++) {
		matrice[i][j] = rand();
	}
}

Ora finalmente possiamo far partire il "cronometro", assegnando, prima alla struttura precedentemente creata, i tv_sec (per i secondi) e tv_usec (per i microsecondi), successivamente la variabile tempoIniziale, con la somma dei secondi, con i microsecondi nella giusta scala. In che senso? Semplicemente per il fatto che li proviamo a stampare ci viene un numero intero, che farebbe sballare di molto i risultati, e perciò andremo a dividerli per 10 alla sesta.

gettimeofday(&tempo, NULL);
tempoIniziale = tempo.tv_sec + (tempo.tv_usec / 1000000.0);

Da questo punto in poi, essendo tutto registrato, possiamo avviare la stampa della matrice, o del pezzo di codice da analizzare:

for(i = 0; i < righe; i++) {
	for(j = 0; j < colonne; j++) {
		printf("matrice[%d][%d] = %d n", i, j, matrice[i][j]);
	}
}

Infine otteniamo il tempo finale, con lo stesso metodo di prima:

gettimeofday(&tempo, NULL);
tempoFinale = tempo.tv_sec + (tempo.tv_usec / 1000000.0);

Per ottenere il tempo di esecuzione, basta effettuare una semplice sottrazione tra i due valori e mostrare il risultato:

printf("Tempo impiegato: %f secondi", tempoFinale - tempoIniziale);

Il risultato sarà simile al seguente (su una matrice di 10x10)

Tempo impiegato: 0.000283 secondi

Mentre su una di 1000x1000:

Tempo impiegato: 15.964256 secondi

 

Conclusioni

Ovviamente il tempo misurato, è puramente indicativo, dato che dipende da molti fattori quali: hardware del computer, programmi in esecuzione, anche sistema operativo in uso! Infatti eseguendo il test sulla stessa macchina ma su Windows, mi ha rilevato un tempo diverso.

In ogni caso l'obiettivo di quest'articolo non è tanto la precisione, ma capire come ottimizzare al meglio il codice per renderlo più veloce e pulito possibile. Una cosa che consiglio è eseguire i benchmark su computer (se ne avete a disposizione) un po' datati, in modo che se l'applicativo gira bene li, non avrà sicuramente problemi su hardware più performante!

Ti potrebbero interessare

I più letti