How to Measure System Time With getrusage Function in C

Jinku Hu Feb 02, 2024
  1. Use the getrusage Function to Measure System Time of Single-Threaded Program
  2. Use the getrusage Function to Measure System Time of Multi-Threaded Program
How to Measure System Time With getrusage Function in C

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
Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

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

Related Article - C Time