How to Measure System Time With getrusage Function in C
-
Use the
getrusage
Function to Measure System Time of Single-Threaded Program -
Use the
getrusage
Function to Measure System Time of Multi-Threaded Program
This article will demonstrate multiple methods about measuring system time with the getrusage
function in C.
Use the getrusage
Function to Measure System Time of Single-Threaded Program
Generally, there are two components of time in any running program. The system time represents the period the program executes in kernel mode and user time, denoting the execution time elapsed in user mode. The sum of both values is called process time, which is a useful measure when optimizing the program’s performance.
The getrusage
function retrieves multiple data points about the process, and one of them is system time represented as struc timeval
object. getrusage
takes an integer value and an address of the struct rusage
object as arguments. The integer specifies which threads/processes should be measured, and it can have the following predefined macro values RUSAGE_SELF
, RUSAGE_CHILDREN
or RUSAGE_THREAD
.
On the other hand, rusage
structure should be declared in advance, and the successful function call stores the corresponding values in it. Since the timeval
structure contains two data members - seconds and microseconds to represent time, we implemented the diffUserTime
and diffSystemTime
functions to calculate elapsed time in seconds.
#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);
}
Output:
loopFunc1 stats:
CPU time: 0.002193 sec user, 0.000000 sec system
loopFunc2 stats:
CPU time: 0.002087 sec user, 0.000190 sec system
Use the getrusage
Function to Measure System Time of Multi-Threaded Program
The getrusage
function can also retrieve the system time used by all threads in the calling process. The RUSAGE_SELF
argument specifies this feature, and it’s mutually usable in a single-threaded program as seen in the previous example.
In the following sample code, we create 16 threads, all of which execute the same loopFunc2
function and terminate. Regardless, the retrieved time by the getrusage
call equals the elapsed time summed in all threads, including those that created them. Meanwhile, if the user wants to measure system time consumed by the calling thread only, RUSAGE_THREAD
can be passed as the first argument. Note though, the RUSAGE_THREAD
value is Linux-specific, and _GNU_SOURCE
must be defined before header files to include it.
#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);
}
Output:
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