C Tutorial: Get and measure elapsed process time

times: get current process times

The times system call gets the user time and system time used by the current process and its children. User time is the time that the processor spent executing instructions in the process. System time is the time that the processor spent in the operating system executing instructions on behalf of the process. The times system call has the following usage:

#include <sys/times.h> clock_t times(struct tms *buf);

Prior to calling times, you'll need to declare a variable of struct tms that the system call will fill in. The struct is defined in sys/times.h as:

struct tms { clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; /* user time of children */ clock_t tms_cstime; /* system time of children */ };

Note that all values are in clock_t units. The actual definition of clock_t is system-dependent. It is typically a long but might be a long long on some systems. All the time values are expressed in "clock ticks." This is an ever-incrementing count of the operating system's "ticks". We can obtain the frequency of these ticks via the sysconf function, which allows us to test and get various system configuration values at runtime. To find the clock frequency, we use:

#include <unistd.h> sysconf(_SC_CLK_TCK);

The call to sysconf(_SC_CLK_TCK) returns a long that indicates the number of ticks per second. Both my Ubuntu Linux and OS X report a value of 100, which means that each tick represents a count of 1/100 second (0.01 s).

Example: reading current process times

This is a small program that does some busy work to use up a bit of CPU time and kernel resources (in the kill_time function) and then prints the time value in clock ticks. Note that we cast the time values into type uintmax_t and call printf with a format of %j. This type represents tha largest available integer. You can usually get away with using long or long long.

/* times: report times used by the current process */ /* Paul Krzyzanowski */ #include <stdio.h> /* defines printf() */ #include <stdlib.h> /* to define exit() */ #include <unistd.h> /* for sysconf() */ #include <stdint.h> /* used for casting clock_t to uintmax_t for printf */ #include <sys/times.h> /* needed for the times() system call */ #include <sys/types.h> #include <sys/stat.h> /* used for open() */ #include <fcntl.h> /* used for open() */ void kill_time(void); int main(int argc, char **argv) { struct tms t; /* the time values will be placed into this struct */ printf("tick frequency is: %lu\n", sysconf(_SC_CLK_TCK)); kill_time(); /* do something to use up some time */ if (times(&t) < 0) { perror("times"); /* error - print a message and exit */ exit(1); } /* print the results */ printf("user time: %ju ticks\n", (uintmax_t) t.tms_utime); printf("system time: %ju ticks\n", (uintmax_t) t.tms_stime); printf("chidren - user time: %ju ticks\n", (uintmax_t) t.tms_cutime); printf("chidren - system time: %ju ticks\n", (uintmax_t) t.tms_cstime); exit(0); } void kill_time(void) { long long i, j; /* use these in a loop to kill time */ int fd; char buf[2048]; printf("doing some cpu wasting stuff\n"); for (i=0; i<100000000; i++) j += i; printf("doing some kernel wasting stuff\n"); /* do some stuff to waste system time */ if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { perror("open"); exit(1); } for (i=0; i < 1000; i++) read(fd, buf, 2048); close(fd); }

Download this file

Save this file by control-clicking or right clicking the download link and then saving it as times.c.

Compile this program via:

gcc -o times times.c

If you don't have gcc, You may need to substitute the gcc command with cc or another name of your compiler.

Run the program:

./times

Measure elapsed time between two points in your code

The times system call returns the current tick count. This is an ever-incrementing number from some arbitrary point in the past. While this on its own is not useful, we can use it to mark a starting and ending time. The difference between these two times is the elapsed time. To do this, we don't care about the data placed in the tms struct. Linux does reasonable error checking and allows us to call times with a null pointer as a parameter. OS X, however, tries to write time values into a structure, which causes the program to die due to an attempt to dereference a null pointer. It is safer to pass a struct tms pointer that we will simply ignore.

Example: measuring elapsed time

In the last example, we ignored the return value from a call to times. In this program, we ignore the struct tms parameter to times but record a start time and a stop time. Between these two times, it calls sleep(2) to sleep for two seconds. It then prints the result in ticks and in seconds. To convert ticks to seconds, you simply divide the elapsed tick count by the frequency obtained from sysconf(_SC_CLK_TCK).

Note that we ignore the case of the clock overflowing and wrapping back through 0 during our test. Good programming would dictate that we take this into account. In practice, the value of clock_t on my 64-bit processors (my Linux and Mac systems) is an unsigned 64-bit integer with a clock frequency of 100 ticks per seconds. That corresponds to a clock wraparound period of 1.8×1017 seconds or 5.8×109 years. I'll take my chances that this will not happen. With 32 bit machines, you need to be more careful since the wrap-around time with a 100 Hz clock will be under two years and far less with a higher frequency clock. If you want to write portable code, you'll need to be aware that wrap-around can be a very real possibility on some architectures.

/* times: use times() to get the elapsed time */ /* Paul Krzyzanowski */ #include <stdio.h> /* declares printf() */ #include <stdlib.h> /* declares exit() */ #include <unistd.h> /* declares sysconf() */ #include <stdint.h> /* used for casting clock_t to uintmax_t for printf */ #include <sys/times.h> /* needed for the times() system call */ int main(int argc, char **argv) { struct tms t; /* parameter to times() - we'll ignore it */ unsigned long freq; /* clock frequency */ uintmax_t start; /* starting tick value */ uintmax_t end; /* ending tick value */ printf("sizeof(clock_t) = %lu\n", sizeof(clock_t)); printf("tick frequency is: %lu\n", freq=sysconf(_SC_CLK_TCK)); if ((start = times(&t)) < 0) { /* mark the start time */ perror("times"); /* error - print a message and exit */ exit(1); } sleep(2); /* sleep for two seconds */ if ((end = times(&t)) < 0) { /* mark the end time */ perror("times"); /* error - print a message and exit */ exit(1); } printf("elapsed time: %ju - %ju = %ju ticks\n", end, start, end-start); printf("elapsed time: %5.2f seconds\n", (double)(end-start)/freq); exit(0); }

Download this file

Save this file by control-clicking or right clicking the download link and then saving it as elapsed.c.

Compile this program via:

gcc -o elapsed elapsed.c

If you don't have gcc, You may need to substitute the gcc command with cc or another name of your compiler.

Run the program:

./elapsed

The clock function

The clock library function (not system call) provides an analogous approach to the return value of times to mark start and end times. Clock takes no parameter and returns an estimate of the processor time used by the running process. The starting time value is undefined so you need to resort to reading the start and end times and then computing the difference, just as we did with times.

The big difference between measuring with clock and times is that clock measures approximate process time used while times returns overall elapsed time. If you try the above program that sleeps for two seconds with clock, you will get a value of 0 elapsed seconds. Basically, the call to sleep put the process to sleep and there was no CPU time used on behalf of the process during that time.

While clock returns its result in the same clock_t data type as times, the scale is different. The POSIX requires that the result be scaled by a value CLOCKS_PER_SEC, which is always defined as one million (1000000), regardless of the actual clock resolution. Note that the finer scale does not mean that clock will always produce more accurate results. The accuracy depends on the implementation. Also note that the high value of CLOCKS_PER_SEC will cause the clock to wrap around to 0 approximately every 72 minutes on a 32-bit processor.

/* times: use clock() to get the elapsed time */ #include <stdio.h> /* declares printf() */ #include <stdlib.h> /* declares exit() */ #include <stdint.h> /* used for casting clock_t to uintmax_t for printf */ #include <time.h> /* needed for the clock() library function */ int main(int argc, char **argv) { unsigned long freq; /* clock frequency */ uintmax_t start; /* starting tick value */ uintmax_t end; /* ending tick value */ start = clock(); /* mark the start time */ sleep(2); /* sleep for two seconds */ end = clock(); /* mark the end time */ printf("elapsed time: %ju - %ju = %ju ticks\n", end, start, end-start); printf("elapsed time: %8.6f seconds\n", (double)(end-start)/CLOCKS_PER_SEC); exit(0); }

Download this file

Save this file by control-clicking or right clicking the download link and then saving it as clock.c.

Compile this program via:

gcc -o clock clock.c

If you don't have gcc, You may need to substitute the gcc command with cc or another name of your compiler.

Run the program:

./clock

The gettimeofday system call

As if we didn't have enough mechanisms to compute time intervals, we can also use the gettimeofday system call. It has the following usage:

#include <sys/time.h> int gettimeofday(struct timeval *tv, struct timezone *tz);

This function, as its name implies, measures the time of day. It returns its value as the number of seconds and microseconds since the Epoch, which is defined as midnight (0:00) January 1, 1970 UTC (Greenwich Mean Time). This value is returned in the first parameter, which is a pointer to a structure that contains two fields: seconds and microseconds. The second parameter identifies the time zone but this is obsolete and should not be used. Keeping track of elapsed seconds avoids the problems of time jumps due to daylight saving time or timezone changes. Those can be added in if a printable calendar time is ever required (see the ctime function).

As with clock, note that the precision is limited by the kernel's timekeeping granularity. Just because the data structure reads microseconds does not imply that you will get microsecond accuracy.

Here's an example of using gettimeofday. You'll note that we measure the overall delay. Expect the delay to be greater than 2 seconds (2,000,000 microseconds) since we show a greater resolution than we do with times.

/* times: use gettimeofday() to get the elapsed time */ /* Paul Krzyzanowski */ #include <stdio.h> /* declares printf() */ #include <stdlib.h> /* declares exit() */ #include <stdint.h> /* used for casting clock_t to uintmax_t for printf */ #include <sys/time.h> /* needed for the gettimeofday() system call */ int main(int argc, char **argv) { struct timeval start; /* starting time */ struct timeval end; /* ending time */ unsigned long e_usec; /* elapsed microseconds */ gettimeofday(&start, 0); /* mark the start time */ sleep(2); /* sleep for 2 seconds */ gettimeofday(&end, 0); /* mark the end time */ /* now we can do the math. timeval has two elements: seconds and microseconds */ e_usec = ((end.tv_sec * 1000000) + end.tv_usec) - ((start.tv_sec * 1000000) + start.tv_usec); printf("elapsed time: %lu microseconds\n", e_usec); exit(0); }

Download this file

Save this file by control-clicking or right clicking the download link and then saving it as gettimeofday.c.

Compile this program via:

gcc -o gettimeofday gettimeofday.c

If you don't have gcc, You may need to substitute the gcc command with cc or another name of your compiler.

Run the program:

./gettimeofday

Beware!

The problem with using a time-of-day report such as the gettimeodday system call is that the time-of-day clock may be spontaneously adjusted to keep the system's time of day synchronized with UTC time (for example, via the Network Time Protocol, NTP). Whenever the time is adjusted, your measurement of the interval between start and stop times becomes invalid and, unless you notice something bizarre such as an end time that is less than the start time, you will not even know that an adjustment took place.

The gettimeofday system call is considered obsolete but is still supported on most POSIX systems. The replacement is clock_gettime but it is not broadly supported yet (OS X, does not have it as of February 2014, for example).

For high-resolution benchmarking, the recommended approach, if available, is to use clock_gettime.

Recommended

The Practice of Programming

 

The C Programming Language

 

The UNIX Programming Environment