Misurare l'ora del sistema con la funzione getrusage in C
-
Utilizzare la funzione
getrusage
per misurare il tempo di sistema di un programma a thread singolo -
Utilizzare la funzione
getrusage
per misurare il tempo di sistema del programma multi-thread
Questo articolo mostrerà diversi metodi sulla misurazione del tempo di sistema con la funzione getrusage
in C.
Utilizzare la funzione getrusage
per misurare il tempo di sistema di un programma a thread singolo
In generale, ci sono due componenti del tempo in qualsiasi programma in esecuzione. Il tempo di sistema rappresenta il periodo di esecuzione del programma in modalità kernel e il tempo utente, indicando il tempo di esecuzione trascorso in modalità utente. La somma di entrambi i valori è chiamata tempo di processo, che è una misura utile quando si ottimizzano le prestazioni del programma.
La funzione getrusage
recupera più punti dati sul processo, e uno di questi è l’ora di sistema rappresentata come oggetto struc timeval
. getrusage
accetta un valore intero e un indirizzo dell’oggetto struct rusage
come argomenti. L’intero specifica quali thread / processi devono essere misurati e può avere i seguenti valori macro predefiniti RUSAGE_SELF
, RUSAGE_CHILDREN
o RUSAGE_THREAD
.
D’altra parte, la struttura rusage
dovrebbe essere dichiarata in anticipo e la chiamata di funzione riuscita memorizza i valori corrispondenti in essa. Poiché la struttura timeval
contiene due membri di dati - secondi e microsecondi per rappresentare il tempo, abbiamo implementato le funzioni diffUserTime
e diffSystemTime
per calcolare il tempo trascorso in secondi.
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <unistd.h>
enum { NUM_ITERS = 1000000 };
void loopFunc1(size_t num) {
int tmp = 0;
for (int i = 0; i < num; ++i) {
tmp += 1;
}
}
void *loopFunc2(size_t num) {
for (int i = 0; i < num; ++i) {
getpid();
}
return NULL;
}
float diffUserTime(struct rusage *start, struct rusage *end) {
return (end->ru_utime.tv_sec - start->ru_utime.tv_sec) +
1e-6 * (end->ru_utime.tv_usec - start->ru_utime.tv_usec);
}
float diffSystemTime(struct rusage *start, struct rusage *end) {
return (end->ru_stime.tv_sec - start->ru_stime.tv_sec) +
1e-6 * (end->ru_stime.tv_usec - start->ru_stime.tv_usec);
}
int main() {
struct rusage start, end;
getrusage(RUSAGE_SELF, &start);
loopFunc1(NUM_ITERS);
getrusage(RUSAGE_SELF, &end);
printf("loopFunc1 stats:\n");
printf(" CPU time: %.06f sec user, %.06f sec system\n",
diffUserTime(&start, &end), diffSystemTime(&start, &end));
getrusage(RUSAGE_SELF, &start);
loopFunc1(NUM_ITERS);
getrusage(RUSAGE_SELF, &end);
printf("loopFunc2 stats:\n");
printf(" CPU time: %.06f sec user, %.06f sec system\n",
diffUserTime(&start, &end), diffSystemTime(&start, &end));
exit(EXIT_SUCCESS);
}
Produzione:
loopFunc1 stats:
CPU time: 0.002193 sec user, 0.000000 sec system
loopFunc2 stats:
CPU time: 0.002087 sec user, 0.000190 sec system
Utilizzare la funzione getrusage
per misurare il tempo di sistema del programma multi-thread
La funzione getrusage
può anche recuperare l’ora di sistema usata da tutti i thread nel processo chiamante. L’argomento RUSAGE_SELF
specifica questa caratteristica, ed è utilizzabile reciprocamente in un programma a thread singolo come si è visto nell’esempio precedente.
Nel seguente codice di esempio, creiamo 16 thread, che eseguono tutti la stessa funzione loopFunc2
e terminano. Indipendentemente da ciò, il tempo recuperato dalla chiamata getrusage
è uguale al tempo trascorso sommato in tutti i thread, inclusi quelli che li hanno creati. Nel frattempo, se l’utente vuole misurare il tempo di sistema consumato solo dal thread chiamante, RUSAGE_THREAD
può essere passato come primo argomento. Nota comunque, il valore RUSAGE_THREAD
è specifico di Linux e _GNU_SOURCE
deve essere definito prima dei file di intestazione per includerlo.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <threads.h>
#include <unistd.h>
enum { NUM_ITERS = 1000000, NUM_THREADS = 16 };
void *loopFunc2(size_t num) {
for (int i = 0; i < num; ++i) {
getpid();
}
return NULL;
}
float diffUserTime(struct rusage *start, struct rusage *end) {
return (end->ru_utime.tv_sec - start->ru_utime.tv_sec) +
1e-6 * (end->ru_utime.tv_usec - start->ru_utime.tv_usec);
}
float diffSystemTime(struct rusage *start, struct rusage *end) {
return (end->ru_stime.tv_sec - start->ru_stime.tv_sec) +
1e-6 * (end->ru_stime.tv_usec - start->ru_stime.tv_usec);
}
int main() {
struct rusage start, end;
thrd_t threads[NUM_THREADS];
int rc;
getrusage(RUSAGE_SELF, &start);
for (int i = 0; i < NUM_THREADS; i++) {
rc = thrd_create(&threads[i], (thrd_start_t)loopFunc2, (void *)NUM_ITERS);
if (rc == thrd_error) {
perror("[ERROR] thrd_create() call failed\n");
}
}
loopFunc2(NUM_ITERS);
getrusage(RUSAGE_SELF, &end);
printf("loopFunc2 stats:\n");
printf(" CPU time: %.06f sec user, %.06f sec system\n",
diffUserTime(&start, &end), diffSystemTime(&start, &end));
exit(EXIT_SUCCESS);
}
Produzione:
loopFunc2 stats:
CPU time: 0.599556 sec user, 0.233000 sec system
Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.
LinkedIn Facebook