What is the proper way to use clock_gettime()?
Asked Answered
B

1

16

I was trying out this function in a C program, and it keeps printing the wrong time. This is my code at the moment:

#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/resource.h>

int main( int argc, char **argv ){
    struct timespec start, finish;
    clock_gettime( CLOCK_REALTIME, &start );
    sleep( 1 );
    clock_gettime( CLOCK_REALTIME, &finish );
    printf( "%f\n", ((double) (finish.tv_nsec - start.tv_nsec))/((double) 100000) );
    return 0;
}

I'm not sure if this is an anomaly caused by rounding errors when converting to a double or if I'm using the clock_gettime() function incorrectly, but I expected it to output 1 second and instead it outputs 1.27 seconds.

Burst answered 10/12, 2018 at 14:48 Comment(5)
1 nanosecond is 1000000000 seconds. Also if you start at second 33.433 and stop at second 34.42991, the difference finish.tv_nsec - start.tv_nsec is negativeMikey
That's not a reliable time delta calculation. You need to compute using the whole seconds as well as the nanoseconds in general, though you'll often get away with it. The number output is tenths of milliseconds — that's plausible for the overhead of system calls and scheduling. But: you ignore the difference of one whole second recorded in the tv_sec elements of the time specs.Adrell
So is tv_nsec the number of nanoseconds modulo the second?Burst
That would explain why the number was too small when I divided by 1 billion.Burst
The tv_nsec is the number of nanoseconds within the current second. It ranges (in theory) between 0 and 999,999,999. This allows an integer number of whole seconds to be stored in tv_sec and a fraction of a second to be stored in tv_nsec. Actual resolution is another issue: see clock_getres() for that. On a Mac, for instance, the resolution is microseconds, even though those are expressed in nanoseconds.Adrell
A
20

You need to take into account the tv_sec member of the structure when calculating the time difference between two values returned by clock_gettime().

The tv_nsec is the number of nanoseconds within the current second. It ranges (in theory) between 0 and 999,999,999. This allows an integer number of whole seconds to be stored in tv_sec and a fraction of a second to be stored in tv_nsec. Actual resolution is another issue: see clock_getres() for that. On a Mac, for instance, the resolution is microseconds, even though those are expressed in nanoseconds.

Consider using code like this:

#include <stdio.h>
#include <time.h>
#include <unistd.h>

enum { NS_PER_SECOND = 1000000000 };

void sub_timespec(struct timespec t1, struct timespec t2, struct timespec *td)
{
    td->tv_nsec = t2.tv_nsec - t1.tv_nsec;
    td->tv_sec  = t2.tv_sec - t1.tv_sec;
    if (td->tv_sec > 0 && td->tv_nsec < 0)
    {
        td->tv_nsec += NS_PER_SECOND;
        td->tv_sec--;
    }
    else if (td->tv_sec < 0 && td->tv_nsec > 0)
    {
        td->tv_nsec -= NS_PER_SECOND;
        td->tv_sec++;
    }
}

int main(void)
{
    struct timespec start, finish, delta;
    clock_gettime(CLOCK_REALTIME, &start);
    sleep(1);
    clock_gettime(CLOCK_REALTIME, &finish);
    sub_timespec(start, finish, &delta);
    printf("%d.%.9ld\n", (int)delta.tv_sec, delta.tv_nsec);
    return 0;
}

When run (as cgt61), I get results like:

$ cgt61
1.004930000
$ cgt61
1.004625000
$ cgt61
1.003023000
$ cgt61
1.003343000
$

This was tested on a Mac; you can see that the final three digits are always zeros. In a Linux VM (Ubuntu 18.04 on a Mac), I had to add #define _POSIX_C_SOURCE 200809L to the code (because I compile with -std=c11; if I used -std=gnu11, I would have been OK), and the output was:

$ ./cgt61
1.000589528
$
Adrell answered 10/12, 2018 at 15:10 Comment(3)
In this if condition - (td->tv_sec < 0 && td->tv_nsec > 0), is it possible that this td->tv_sec < 0 will ever result in true?Holmann
You won't get tv_sec < 0 from a call to clock_gettime() (unless the system clock is set horribly wrong), but if the user has created a time before 1970-01-01 00:00:00Z, then the tv_sec value will be negative.Adrell
Fair enough. if the user has created a time before 1970-01-01 00:00:00Z, then.... - its only possible when someone explicitly do it on the a system otherwise there is no way that this condition td->tv_sec < 0 will result in true.Holmann

© 2022 - 2024 — McMap. All rights reserved.