37
\$\begingroup\$

I need to wait for short intervals of time in my program, but I really don't want to include unistd.h just to use sleep, so I'm doing it this way:

int main()
{
    for(int i=0; i<=100000000000; i++); // <----- note that the loop doesn't do anything 
    return 0;
}

I do this when I don't really have a specific interval to set for sleep.

Is there anything wrong with this? Would this harm the program in any way?

In C I know that if I did include unistd.h, and that if I used sleep(), that the following code would also make the program wait for a short interval of time:

int main()
{
    sleep(2); //or whatever the number (in seconds)
    return 0;
}
\$\endgroup\$
5
  • 10
    \$\begingroup\$ See also What are trade offs for “busy wait” vs “sleep”? \$\endgroup\$ Commented Feb 22, 2014 at 15:42
  • 18
    \$\begingroup\$ Do you have any actual reason for not wanting to type "#include <unistd.h>"? \$\endgroup\$ Commented Feb 22, 2014 at 21:54
  • 5
    \$\begingroup\$ Using 100000000000 for an int is not very portable, some compilers wouldn't like it. \$\endgroup\$
    – QuentinUK
    Commented Feb 23, 2014 at 4:09
  • 3
    \$\begingroup\$ I did the same with a program and it nearly killed the computer. Internally, sleep will take the load off the program and put it really to sleep. \$\endgroup\$
    – Noah
    Commented Feb 23, 2014 at 6:29
  • 1
    \$\begingroup\$ It will likely be optimized out as you wrote it: stackoverflow.com/questions/7083482/… \$\endgroup\$ Commented Jun 26, 2015 at 6:58

9 Answers 9

91
\$\begingroup\$

The biggest problem with using a for-loop to do this is that you are wasting CPU power.

When using sleep, the CPU can, in a sense, take a break (hence the name "sleep") from executing your program. This means that the CPU will be able to run other programs that have meaningful work to do while your program waits.

But in the for-loop the CPU continuously have to do work to increase a variable. For what purpose? Nothing. But the CPU doesn't know that. It's told to increase the variable, so that's what it will do. Meanwhile, other programs aren't given as much time to do their work because your program is taking up time.

Please don't waste CPU power, use the sleep approach.

As stated in a comment, additionally, the compiler may optimize out an empty loop, and the attempt to pause may be lost. So either you will waste CPU power, or you will not pause at all.

\$\endgroup\$
2
  • 58
    \$\begingroup\$ In addition, the compiler may optimize out an empty loop, and the attempt to pause may be lost \$\endgroup\$
    – atk
    Commented Feb 22, 2014 at 19:45
  • 1
    \$\begingroup\$ In further addition, if the compiler left it, it would be bad for scheduling, since the scheduler will see for as a long but active process. \$\endgroup\$
    – OJFord
    Commented Feb 22, 2014 at 23:08
35
\$\begingroup\$

Back in the day, home computers used to have a “turbo” button, which (when activated), made the processor slower. The problem was that games were coded around an input loop that was expected to take a specific number of milliseconds. If the loop was executed faster, the whole game would speed up, becoming unplayable.

Today, computers have vastly different speeds and performance characteristics, so NOPping isn't reliable in any way (will it take half a second? half a year?). Furthermore, compilers are generally free to remove code that doesn't do anything – compiling your snippet under -O3 would hopefully remove that loop.

Don't use an ugly kludge just to achieve better compilation speed. Do it right – consider yourself a code craftsman, not a code monkey (no offense, rolfl).

\$\endgroup\$
2
  • 10
    \$\begingroup\$ No offense taken... ;-) \$\endgroup\$
    – rolfl
    Commented Feb 22, 2014 at 18:37
  • \$\begingroup\$ Eh, my computer is fairly recent and have a Turbo button. However it behaves differently, it does a soft overclock on the GPU. \$\endgroup\$ Commented Feb 23, 2014 at 5:46
26
\$\begingroup\$

This is called 'busywaiting' or 'spinning', and will work but has the following disadvantages:

  1. It will perform 'work' on your program instead of yielding control to OS and other programs - so it doesn't "play nice" with others; on a common computer there are many programs running at the same time that could also like to use that CPU instead.

  2. Busywaiting creates unneeded load on CPU, preventing it from idling - this results in extra heat, fan noise and lower battery life on laptop computers or mobile devices.

  3. The delay is unpredictable - it will vary greatly depending on the computer and also the compiler, which may "optimize it away" to no delay at all, as other answers noted.

In short - it will function, so if it's a one-off script for your own use then it can be okay, but if it's code meant for production use or something that will need to be maintained in the future, then don't do it this way.

\$\endgroup\$
1
  • \$\begingroup\$ +1 for the mention of battery impact, a relatively significant issue when so much computing is done on mobile devices these days. \$\endgroup\$
    – Barmar
    Commented Feb 23, 2014 at 3:26
16
\$\begingroup\$

This technic is called Spinlock or busy waiting. It is implemented for example in Oracle database software to coordinate access to memory structures between different processes (latches).

It seems to be useful if the overhead of sleeping which results from context switching is larger than waste of CPU time when spinning. This is the case if the expected wait time is very short. Very short means some clock cycles and is far less than 2000 ms that you use in your example.

The compiler will not optimize the loop away if it is implemented correctly. This C example using a volatile variable is from busy waiting .

...
volatile int i=0;
...
while (i==0);
...

volatile tells the compiler that the value of the variable i can be changed from outside the current thread and so the compiler does not throw away the while-loop.

Busy waiting is listed as antipattern here. So you should not use it if you are not completely aware of its consequences.

Trying to avoid the inclusion of unistd.h seems not to be a valid reason to use busy waiting.

\$\endgroup\$
15
\$\begingroup\$

The other answers here address the specific issues with your loop, and hopefully have convinced you that it is not the right approach. I want to comment on your motivations.

Why do you want to avoid unistd.h? It is entirely reasonable to include a header for just one function, and in the vast majority of cases there is no compelling reason not to.

If you are concerned about portability, just do something like this:

#if defined(__linux__)
#  include <unistd.h>
#elif defined(_WIN32)
#  include <windows.h>
#  define sleep(s) Sleep((s)*1000)
#endif

Then use sleep(seconds) everywhere.

\$\endgroup\$
11
\$\begingroup\$

Two big reasons why that's a bad idea:

  1. The time spent on a given loop is very dependent on the computer running it. Ever played Quake on a modern computer? The frame rates are through the roof, and I hope that's not a surprise. More powerful computers can perform more calculations in a set time. If your computer takes 10 seconds to complete a loop, a more powerful computer might finish in 5 or less, and an older computer might take significantly longer.

    So, couldn't I just do this?

    #include <Mmsystem.h> //timeGetTime
    void Stall(unsigned long ms)
    {
        unsigned long start = timeGetTime();
        unsigned long passed = 0;
        while (passed < ms)
        passed = timeGetTime() - start;
    }
    

    Yeah, that would ensure that the same time is taken to finish the function. But don't get too excited; I said two big reasons.

  2. Sleep functions set your thread in a sort of "do not disturb" state, allowing your operating system to perform other tasks (handling other threads, cleaning up your thread) while your thread daydreams. While a human could look at the line and interpret it as "do nothing", your computer is wasting power blowing through that empty loop as fast as it can.

    So, I can do this?

    #include <Mmsystem.h> //timeGetTime
    #include <WinBase.h> //Sleep
    void Stall(unsigned long ms)
    {
        unsigned long start = timeGetTime();
        unsigned long passed = 0;
        while (passed < ms)
        {
            passed = timeGetTime() - start;
            Sleep(0);
        }
    }
    

    Better, but it still wastes CPU resources when it doesn't have to. And if there are no other threads running, Sleep(0) immediately returns to your thread, meaning your CPU is still running at maximum speed.

Conclusion: for most applications, stick with sleep functions.

\$\endgroup\$
5
\$\begingroup\$

The biggest single issue with this approach, other than eating processor cycles, is that the for loop will be removed by the optimiser if you enable any level of optimisation in the compiler. Suddenly you find that code that ran just fine in debug mode, stops working.

\$\endgroup\$
1
  • \$\begingroup\$ That is no really a valid argument against the usage of this technic. If programmed carefully this probleem can be avoided. Here is a discussion about avoiding optimization for a piece of code when using gcc: stackoverflow.com/questions/2219829/… help \$\endgroup\$
    – miracle173
    Commented Feb 23, 2014 at 22:02
4
\$\begingroup\$

Avoiding calling certain proper OS API functions just because of reluctance to include a .h-file is not a fair tradeoff. You have to include a .h-file with OS API calls or use assembler instructions. It depends on whether you wish to wait for some event to occur, e.g., a file finished writing or data finished sending via a socket. In this case, it’s better to call an OS API that simply waits for the event to occur. If you need just to wait a certain amount of time, it also depends on how long do you need to wait. If you need to wait up to about 50 thousand CPU cycles, then use a combination of PAUSE and RDTSC instruction, call RDTSC once after calling PAUSE about 500 times. If you need delays longer than 50 thousand of CPU cycles, create a waitable high-resolution timer and wait for it. For windows, there is a function CreateWaitableTimer. You can find an example at https://msdn.microsoft.com/en-us/library/windows/desktop/ms687008(v=vs.85).aspx

If you need to wait at least 1 millisecond or more, you can just call Sleep(). But make sure you call sleep with at least 1 millisecond parameter, don’t call Sleep(0) since it will not let CPU rest while you are waiting.

\$\endgroup\$
1
\$\begingroup\$

I strongly disagree with the accepted answer, which says

The biggest problem with using a for-loop to do this is that you are wasting CPU power.

That is a problem, but it isn't the biggest problem by far. The biggest problem is that int is 32 bits on many platforms, including 64-bit Windows, and 100000000000 is out of the range of a 32-bit int. Depending on how such constants are handled, i may be incremented until it overflows. The behavior of int on overflow is undefined.

Modern optimizing compilers are smart enough to recognize that this code has undefined behavior, but many are not smart enough to do anything sane with that information. Clang, particularly, is notorious for doing crazy things with undefined code, such as emitting no machine language instructions at all for the entire function, not even a ret instruction, leaving the instruction pointer to wander off into no man's land and perhaps execute code from some unrelated function that happens to be nearby in memory.

The answers that imply that the worst thing the compiler could do is delete the loop are wrong. Much worse things can happen.

On platforms where 100000000000 is a valid int, the compiler will probably just delete the loop, so wasting CPU power still isn't the biggest problem on those platforms; the biggest problem is that it just won't work. If you disable optimizations, it will probably work as intended, but writing code that only works when optimizations are disabled is not considered good programming practice, unless the reason is that the optimizer is buggy, which is not the case here.

\$\endgroup\$
1
  • \$\begingroup\$ The 100000000000 was just an example. The question is asking specifically about using the for loop instead of the sleep function regardless of the number you use for the loop. If overflowing was the problem, one could just use long long for example. \$\endgroup\$
    – Mohammad
    Commented Aug 21, 2021 at 18:56

Not the answer you're looking for? Browse other questions tagged or ask your own question.