Saveh
Saveh

Reputation: 41

Sleep C function in CodeBlocks (for Windows) doesn't work properly

I'm trying to do a stopwatch in C ( for Windows ), the code seems to work but the time with the Sleep function doesn't match real time.

Process returned 0 (0x0) execution time : 1.907 s Press any key to continue.

The problem is that the execution time is around 2 seconds but it should be just 1 sec.. just wondering what I am doing wrong since the Sleep function in Windows accepts milliseconds as parameters it should be working.. here is the code

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main()
{

    int milliseconds=0;
    int seconds=0;
    int counter;

    for(counter=0;counter<1000;counter++) {

        Sleep(1);
        milliseconds = milliseconds + 1;
        printf("%d\n",milliseconds);

        if(milliseconds==1000) {
            seconds = seconds + 1;
            milliseconds = 0;
            printf("seconds: %d",seconds);
        }

    }

    return 0;
}

Upvotes: 1

Views: 4331

Answers (3)

T Johnson
T Johnson

Reputation: 824

Requests to the operating system are processed on a best-effort basis, without any response time guarantee. Windows is designed and optimized with performance and throughput in mind for general purpose uses, not for real-time tasks. Your process shares the OS with other processes that also want the attention of the OS. The OS only guarantees that the process suspension will last for at least as long as you requested give or take a clock tick. Plus processing time for looping means that you will get inconsistent results from this code.

Upvotes: 0

Cody Gray
Cody Gray

Reputation: 244742

You cannot use the Sleep function to write a stopwatch. It is not intended for timing anything. All it does is cause a thread to yield the rest of its time slice to other competing threads, allowing them to execute. There is no guarantee about the precise amount of time that your thread will sleep. Its execution may be pre-empted by a higher-priority thread. As per the documentation:

If dwMilliseconds is less than the resolution of the system clock, the thread may sleep for less than the specified length of time. If dwMilliseconds is greater than one tick but less than two, the wait can be anywhere between one and two ticks, and so on.

It goes on to talk about how to increase the accuracy of the sleep interval, but that's not what you want, either. Creating a timer would be more appropriate for your purposes, e.g. using the SetTimer function. Specify a callback function and you will be notified when the time has elapsed. If you needed an extremely accurate timer, you could use a multimedia timer. Tutorials are available here. Probably unnecessary for a simple stopwatch, though.

To obtain time counts and implement your own timing facility, you could call the GetTickCount function. Or use the high-resolution timer APIs for maximum resolution. QueryPerformanceCounter returns the current timestamp, which you divide by the result of QueryPerformanceFrequency.

Upvotes: 1

Alois Kraus
Alois Kraus

Reputation: 13535

You are sleeping with a timeout of 1ms. In effect you are giving up your timeslice for your current thread which is by default 15,6ms. But if you have a WPF application running just like Visual Studio it will be set to 1ms. Sleep will return not earlier than you wanted to sleep so you will wait effectively up to to timeslices which add up to 2s sleep time. If you use a profiler like ETWController you can see the thread waits directly.

enter image description here

There you see that we have 1004 context switch events which did wait on average 1,6ms and not 1ms which you did anticipate. There is a lot more to how the OS scheduler influences how long your sleep takes. The best thing is to measure. See for example SpinWait is dangerous.

When I e.g. close all applications which enforce 1ms system timer I will get a 6,5s sleep duration! To check the true wait time I have used your code with a highres timer to print the true wait time:

#include "stdafx.h"
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <chrono>

int main()
{

    int milliseconds = 0;
    int seconds = 0;
    int counter;

    auto start = std::chrono::high_resolution_clock::now();

    for (counter = 0; counter<1000; counter++) {

        Sleep(1);
        milliseconds = milliseconds + 1;
        //printf("%d\n", milliseconds);

        if (milliseconds == 1000) {
            seconds = seconds + 1;
            milliseconds = 0;
            printf("\nseconds: %d", seconds);
        }

    }

    auto stop = std::chrono::high_resolution_clock::now();
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count();
    printf("Duration: %dms", ms);
    return 0;
}

Here is the output:

ClockRes v2.0 - View the system clock resolution
Copyright (C) 2009 Mark Russinovich
SysInternals - www.sysinternals.com

Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
Current timer interval: 1.000 ms
seconds: 1
Duration: 1713ms

ClockRes v2.0 - View the system clock resolution
Copyright (C) 2009 Mark Russinovich
SysInternals - www.sysinternals.com

Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
Current timer interval: 15.625 ms

seconds: 1
Duration: 6593ms

Upvotes: 1

Related Questions