3

I'm relatively new to C programming and I'm working on a project which needs to be very time accurate; therefore I tried to write something to create a timestamp with milliseconds precision.

It seems to work but my question is whether this way is the right way, or is there a much easier way? Here is my code:

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

void wait(int milliseconds)
{
    clock_t start = clock();
    while(1) if(clock() - start >= milliseconds) break;
}

int main()
{
    time_t now;
    clock_t milli;
    int waitMillSec = 2800, seconds, milliseconds = 0;
    struct tm * ptm;

    now = time(NULL);
    ptm = gmtime ( &now );
    printf("time before: %d:%d:%d:%d\n",ptm->tm_hour,ptm->tm_min,ptm->tm_sec, milliseconds );

    /* wait until next full second */
    while(now == time(NULL));

    milli = clock();
    /* DO SOMETHING HERE */
    /* for testing wait a user define period */
    wait(waitMillSec);
    milli = clock() - milli;

    /*create timestamp with milliseconds precision */
    seconds = milli/CLOCKS_PER_SEC;
    milliseconds = milli%CLOCKS_PER_SEC;
    now = now + seconds;
    ptm = gmtime( &now );
    printf("time after: %d:%d:%d:%d\n",ptm->tm_hour,ptm->tm_min,ptm->tm_sec, milliseconds );
    return 0;
}
Community
  • 1
  • 1
morten
  • 65
  • 1
  • 2
  • 4

3 Answers3

7

The following code seems likely to provide millisecond granularity:

#include <windows.h>
#include <stdio.h>

int main(void) {
    SYSTEMTIME t;
    GetSystemTime(&t); // or GetLocalTime(&t)
    printf("The system time is: %02d:%02d:%02d.%03d\n", 
        t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
    return 0;
}

This is based on http://msdn.microsoft.com/en-us/library/windows/desktop/ms724950%28v=vs.85%29.aspx. The above code snippet was tested with CYGWIN on Windows 7.

For Windows 8, there is GetSystemTimePreciseAsFileTime, which "retrieves the current system date and time with the highest possible level of precision (<1us)."

Your original approach would probably be ok 99.99% of the time (ignoring one minor bug, described below). Your approach is:

  • Wait for the next second to start, by repeatedly calling time() until the value changes.
  • Save that value from time().
  • Save the value from clock().
  • Calculate all subsequent times using the current value of clock() and the two saved values.

Your minor bug was that you had the first two steps reversed.

But even with this fixed, this is not guaranteed to work 100%, because there is no atomicity. Two problems:

  • Your code loops time() until you are into the next second. But how far are you into it? It could be 1/2 a second, or even several seconds (e.g. if you are running a debugger with a breakpoint).

  • Then you call clock(). But this saved value has to 'match' the saved value of time(). If these two calls are almost instantaneous, as they usually are, then this is fine. But Windows (and Linux) time-slice, and so there is no guarantee.

Another issue is the granularity of clock. If CLOCKS_PER_SEC is 1000, as seems to be the case on your system, then of course the best you can do is 1 msec. But it can be worse than that: on Unix systems it is typically 15 msecs. You could improve this by replacing clock with QueryPerformanceCounter(), as in the answer to timespec equivalent for windows, but this may be otiose, given the first two problems.

Community
  • 1
  • 1
Joseph Quinsey
  • 9,553
  • 10
  • 54
  • 77
  • 1
    Thanks a lot for your fast response. I will try this when im back at my working machine. Until now I didn't dare to use any windows headers. Unfortunately I have to use Windows xp and Visual Studio 6.0, hopefully this wont be a problem. – morten Feb 20 '14 at 09:44
  • @morten: The msdn link says `GetSystemTime` is available on Windows 2000 and up, and I have just verified that it seems to work on my XP Home SP3 machine. Good luck, and tell us what you end up doing. – Joseph Quinsey Feb 20 '14 at 13:38
  • 1
    just wanted to say thank you i just implemented your idea and everything works fine – morten Mar 05 '14 at 13:46
1

Clock periods are not at all guaranteed to be in milliseconds. You need to explicitly convert the output of clock() to milliseconds.

t1 = clock();
// do something
t2 = clock();
long millis = (t2 - t1) * (1000.0 / CLOCKS_PER_SEC);
Zhehao Mao
  • 1,789
  • 13
  • 13
0

Since you are on Windows, why don't you just use Sleep()?

Markku K.
  • 3,840
  • 19
  • 20
  • Sadly I didn't knew of it. As I said, Im quite new to c programming and it took me rather long to come up with a waiting function :) – morten Feb 20 '14 at 09:46
  • @morten - Busy loops unnecessarily consume a lot of CPU. Non-zero Sleep() gives up the current process' time slice back to the kernel so that it can schedule another process to run. – CubicleSoft Dec 05 '15 at 07:05