gettimeofday() microsecond not limited to below second

1.9k Views Asked by At

When I output the microseconds field for gettimeofday(), I notice that the microsecond field is larger than 1,000,000. Does anyone know why this is? And does this imply that I've been interpreting gettimeofday() wrong?

For the record, my assumption is that the current time (in microseconds) according to gettimeofday() is the following:

struct timeval ts;
gettimeofday(&ts, NULL); 
printf("%zu", ts.tv_sec * 1000000 + ts.tv_usec);

Edit: Here is the code that is causing the problem. After the comments below, the printf() might be at fault.

struct timeval curr_time;
gettimeofday(&curr_time, NULL);
printf("Done-arino! Onto the matrix multiplication (at %zu s, %03zu ms)\n", curr_time.tv_sec, curr_time.tv_usec);

// Matrix Multiplication
struct timeval start_tv, end_tv, elapsed_tv;
gettimeofday(&start_tv, NULL);
for (i = 0; i < N; i++)
    for (j = 0; j < N; j++)
        for (k = 0; k < N; k++)
            C[i][j] += A[i][k] * B[k][j];
gettimeofday(&end_tv, NULL);
timersub(&end_tv, &start_tv, &elapsed_tv);

// Print results
printf("Elapsed time: %zu s, %03zu ms\n", elapsed_tv.tv_sec, elapsed_tv.tv_usec / 1000);
3

There are 3 best solutions below

0
On

After a successful to gettimeofday, yes, tv_usec is guaranteed to be strictly less than 1000000.

If you (think you) saw a value of 1000000 or greater, then yes, it's likely you were doing something wrong.

A frequent mistake is to add or subtract two struct timeval values naively, without implementing proper carry or borrow between the tv_sec and tv_usec fields, and this can easily lead to (mistaken and wrong) values in tv_usec greater than 1000000. (In your edited post you mention subtracting timespecs, but you're using the system-supplied timersub function which ought to get the borrow right.)

If you were using a struct timespec instead of struct timeval, and if a leap second were going on, and if you were (miraculously) using an OS kernel that implemented the CLOCK_UTC clock type proposed by Markus Kuhn at https://www.cl.cam.ac.uk/~mgk25/posix-clocks.html, you'd see tv_nsec values greater than 1000000000, but that's a lot of "if"s. (And to my knowledge no kernel in widespread use has ever implemented CLOCK_UTC.)

14
On

You'll need to show some more convincing code, and identify the platform on which you run into the problem.

For example:

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

int main(void)
{
    while (1)
    {
        struct timeval ts;
        if (gettimeofday(&ts, 0) == 0 && ts.tv_usec >= 1000000)
            printf("%lu s; %lu µs\n", (long)ts.tv_sec, (long)ts.tv_usec); 
    }
    return 0;
}

The very busy loop is a tad irksome; maybe you should use nanosleep() to sleep for a microsecond or two on every iteration:

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

int main(void)
{
    while (1)
    {
        struct timeval tv;
        if (gettimeofday(&tv, 0) == 0 && tv.tv_usec >= 1000000)
            printf("%lu s; %lu µs\n", (long)tv.tv_sec, (long)tv.tv_usec); 
        struct timespec ts = { .tv_sec = 0, .tv_nsec = 2000 };
        nanosleep(&ts, 0);
    }
    return 0;
}

Or, including a progress meter to demonstrate that the code is running:

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

    int main(void)
    {
        size_t loop_count = 0;
        size_t line_count = 0;

        while (1)
        {
            struct timeval tv;
            if (gettimeofday(&tv, 0) == 0 && tv.tv_usec >= 1000000)
                printf("%lu s; %lu µs\n", (long)tv.tv_sec, (long)tv.tv_usec); 
            struct timespec ts = { .tv_sec = 0, .tv_nsec = 2000 };
            nanosleep(&ts, 0);
            if (++loop_count > 100000)
            {
                loop_count = 0;
                putchar('.');
                line_count++;
                if (line_count >= 50)
                {
                    putchar('\n');
                    line_count = 0;
                }
                fflush(stdout);
            }
        }
        return 0;
    }

timersub()

On an Ubuntu 16.04 LTS VM, I can find file /usr/include/x86_64-linux-gnu/sys/time.h containing a macro:

# define timersub(a, b, result)                                               \
  do {                                                                        \
    (result)->tv_sec = (a)->tv_sec - (b)->tv_sec;                             \
    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                          \
    if ((result)->tv_usec < 0) {                                              \
      --(result)->tv_sec;                                                     \
      (result)->tv_usec += 1000000;                                           \
    }                                                                         \
  } while (0)

All the indications I can see are that tv_usec is an __u32, an unsigned quantity. If that's the case, then the < 0 condition will never be true and you may sometimes see grotesquely large positive values instead. YMMV, of course.

Even my mileage varies

Further scrutiny shows that while there are headers that seem to use __u32 for tv_usec, those are not the main system headers.

/usr/include/linux/time.h:  __kernel_suseconds_t     tv_usec;   /* microseconds */
/usr/include/linux/can/bcm.h:   long tv_usec;
/usr/include/drm/exynos_drm.h:  __u32           tv_usec;
/usr/include/drm/exynos_drm.h:  __u32           tv_usec;
/usr/include/drm/vmwgfx_drm.h:  uint32_t tv_usec;
/usr/include/drm/drm.h: __u32 tv_usec;
/usr/include/rpc/auth_des.h:    uint32_t tv_usec;           /* Microseconds.  */
/usr/include/valgrind/vki/vki-darwin.h:#define vki_tv_usec tv_usec
/usr/include/valgrind/vki/vki-linux.h:  vki_suseconds_t tv_usec;    /* microseconds */
/usr/include/rpcsvc/rstat.x:    unsigned int tv_usec;   /* and microseconds */
/usr/include/rpcsvc/rstat.h:    u_int tv_usec;
/usr/include/x86_64-linux-gnu/bits/utmpx.h:    __int32_t tv_usec;       /* Microseconds.  */
/usr/include/x86_64-linux-gnu/bits/time.h:    __suseconds_t tv_usec;    /* Microseconds.  */
/usr/include/x86_64-linux-gnu/bits/utmp.h:    int32_t tv_usec;      /* Microseconds.  */
/usr/include/x86_64-linux-gnu/sys/time.h:   (ts)->tv_nsec = (tv)->tv_usec * 1000;                           \
/usr/include/x86_64-linux-gnu/sys/time.h:   (tv)->tv_usec = (ts)->tv_nsec / 1000;                           \
/usr/include/x86_64-linux-gnu/sys/time.h:# define timerisset(tvp)   ((tvp)->tv_sec || (tvp)->tv_usec)
/usr/include/x86_64-linux-gnu/sys/time.h:# define timerclear(tvp)   ((tvp)->tv_sec = (tvp)->tv_usec = 0)
/usr/include/x86_64-linux-gnu/sys/time.h:   ((a)->tv_usec CMP (b)->tv_usec) :                         \
/usr/include/x86_64-linux-gnu/sys/time.h:    (result)->tv_usec = (a)->tv_usec + (b)->tv_usec;                 \
/usr/include/x86_64-linux-gnu/sys/time.h:    if ((result)->tv_usec >= 1000000)                        \
/usr/include/x86_64-linux-gnu/sys/time.h:   (result)->tv_usec -= 1000000;                         \
/usr/include/x86_64-linux-gnu/sys/time.h:    (result)->tv_usec = (a)->tv_usec - (b)->tv_usec;                 \
/usr/include/x86_64-linux-gnu/sys/time.h:    if ((result)->tv_usec < 0) {                         \
/usr/include/x86_64-linux-gnu/sys/time.h:      (result)->tv_usec += 1000000;                          \

It's worrying to see any code using an unsigned type for a member with that name, but that doesn't mean it is happening for code using struct timeval and timersub().

This code:

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

int main(void)
{
    struct timeval t = { .tv_sec = 0, .tv_usec = -1 };
    printf("%ld %ld\n", (long)t.tv_sec, (long)t.tv_usec);
    return 0;
}

compiled for 64-bit (so long is big enough to hold anything that tv_usec could be defined as) prints 0 -1 as it should. It would be possible to initialize the tv_usec member to 0, decrement it, and verify that it is negative, and various other related tests.

So, the problem isn't as simple as "timersub() is wrong" — which is an immense relief.

0
On

Your printf formats are suspect, and could be causing this problem.

The %zu format is for printing size_t values. But neither tv_sec nor tv_usec has type size_t.

On a modern system, size_t is likely to be 64 bits. But if either tv_sec or tv_usec is not, printf will end up printing those values quite wrongly.

I changed your printfs to

printf("Done-arino! Onto the matrix multiplication (at %ld s, %03u ms)\n",
        curr_time.tv_sec, curr_time.tv_usec);

and

printf("Elapsed time: %ld s, %03u ms\n",
        elapsed_tv.tv_sec, elapsed_tv.tv_usec / 1000);

These won't necessarily be correct for you, though — it depends on your system's specific choices for tv_sec and tv_usec.

The general and portable way to print a value of an implementation-defined type like this is to explicitly cast it to the largest type it can be, then use the printf format corresponding to the cast-to type. For example:

printf("Done-arino! Onto the matrix multiplication (at %ld s, %03ld ms)\n",
        (long)curr_time.tv_sec, (long)curr_time.tv_usec);

printf("Elapsed time: %ld s, %03ld ms\n",
        (long)elapsed_tv.tv_sec, (long)elapsed_tv.tv_usec / 1000);

The cast might or might not be a no-op, but the point is that, no matter what the original type was, you end up with something that matches what you've told printf to expect.