Re: Why INFINITE loop in a thread occupy so much CPU time??
- From: Joseph M. Newcomer <newcomer@xxxxxxxxxxxx>
- Date: Mon, 19 Nov 2007 11:52:16 -0500
See below...
On Sat, 17 Nov 2007 23:59:56 GMT, Geoff <geoff@xxxxxxxxxxxxxxx> wrote:
On Sun, 18 Nov 2007 05:24:06 +1000, "Ian Semmel"****
<anyone@xxxxxxxxxxxxxxxxx> wrote:
many
On Wed, 14 Nov 2007 22:16:44 -0800 (PST), lostlander
<lostlander.tom@xxxxxxxxx> wrote:
I've got a thread:
threadFunc(LPVOID pdata)
{
while(1)......
}
why it takes so much cpu time on windows??
What did you expect to happen? Threads that don't yield consume as
CPU
cycles as the OS will give them, subject to things like relative
priority,
dynamic priority adjustment, and so forth.
--
Doug Harrison
Visual C++ MVP
This is really a Windows thing rather than a programming problem.
There is nothing intrinsically wrong with writing CPU-intensive
functions. Operating Systems exist (and have existed for decades) that
handle them OK. They should just be running at the lowest priority.
Functions such as Yield, which force a programmer to usurp the role of
the operating system are not a solution in my opinion.
The not uncommon situation in Windows whereby one rogue program can
screw up the entire system is far from satisfactory.
It depends on what you call "CPU Utilization" too. Windows regards the
Idle process as "idle CPU" and any process that is not the "idle task"
is "utilized CPU" so 100% CPU utilization on Windows mostly just means
there is a task running in the system that is not the idle task and
that task is not "yielding" to the scheduler and it is being preempted
and immediately rescheduled and dispatched. The idle task in Windows
is the task responsible for zeroing free pages and I believe once it
has done that it actually halts the processor(s). See Intel's comments
on HLT and documentation on power control for mobile processors. One
cannot execute HLT in user-land.
The empty loop: for(;;); assembles to:
here: jmp short here
and this will generate 100% cpu utilization but won't degrade the
system at all. On a P4 system it actually generates 50% since the HT
processor is running idle task in alternation with the looping task.
The real problem arises when you have a thread which is competing "unfairly". Some years
ago, when I was doing RS-485 multidrop communication, the problem arose because RS-485 is
half-duplex, that is, the output line drivers are three-state (high, low, off). If you
are sending data, the line drivers are sending high/low sequences; if they are on, I think
they are always held low (< -3.2 volts, or something in that range). So if a line driver
is on at one interface, the other interface cannot successfully send data.
The trick was to make sure I disabled the line drivers after transmitting data. This was
a problem. If you relied (in those days) on the Microsoft code, it would send the
all-characters-sent notification NOT when all the characters were sent, but when it had
successfully placed the last character into the UART buffer. If you just dropped the line
at that point, you would turn off the line drivers in the middle of the character. If you
waited too long, they were still on when the remote device tried to send. So what I did
was, upon completion of a WriteFile, spin for a bit of time (I couldn't use Sleep()
because this was originally on MS-DOS, that is, Win9x, with a 55ms timer tick). But I
could be preempted, so sometimes it would still lose the reply. So the trick was to set
thread priority high. Then, due to a bug in my code, I ended up compute-bound. Ugly. It
was nearly impossible to shut the interface down. Of course, once I fixed the bug, it
worked correctly, but it pointed out that messing with thread priorities is a Really Bad
Idea unless done carefully, and I had not been careful enough.
*****
****
System degradation doesn't seem to occur in Windows until you are
overburdening the kernel or GUI threads.
Or making the kind of error I made
****
****
Yield is obsolete and generates no code. Neither does it remedy CPU
utilization.
I had long since forgotten about Yield, but indeed I've seen people use it as if it had
some purpose, which it doesn't.
****
*****
Even using SetPriorityClass to set the application to
IDLE_PRIORITY_CLASS will only make it equal in priority to the idle
task and cause apparent CPU utilization. Messing with process or
thread priorities is generally a bad idea since priority inversion or
deadlock can occur if you are not careful.
As I tell my students, "messing with thread priorities is a very dangerous activity" (I
tell them the above story). It should be done VERY carefully, with EXTREMELY CAREFUL
consideration of the consequences. The SetThreadAffinity trick is really neat, because it
allows you to retain one CPU for pretty much everything else, while offloading the
compute-bound burden in a way that maximizes throughput without interfering with the user
interface at all (all the other threads that are interfaces threads will naturally migrate
to CPU0)
*****
****
MSDN states Sleep(milliseconds) is the preferred method for
applications that don't create windows to yield time back to the
system. They also state that Sleep(0) is the method. This appears to
be wrong. Sleep(1) on Windows XP will cause low CPU utilization and
appears to yield back to idle for the remainder of the scheduler
interval. Sleep(0) still runs the thread and causes high CPU
utilization.
Sleep(0) will actually cause the thread to yield and move to the end of the queue it was
in. I demonstrate this in my System Programming class because we write a multithreaded
file printer. On a uniprocessor, the lines of the files come out "clumped" in groups of
10-20 lines, more or less, which shows the round-robin threading and the effects of
timeslicing. But if they add a Sleep(0) right after the output call, the lines come out
fully interlaced, file1, file2, file3, file1, file2, file3, and so on (and when it fails,
I ask them to explain why we missed a line of, say, file2...yes, it missed its turn
because it was blocked on file I/O!). However, if the threads are actually fully
compute-bound, Sleep(0) just adds scheduler overhead without actually gaining anything
about utilization. Sleep(1) == Sleep(15) in most situations, since all intervals are
rounded up to the next timer tick quantum.
****
****
The fact is, the simplest method for writing an infinite loop with low
CPU utilization under Windows is:
int main(void)
{
for(;;)
Sleep(1);
return 0;
}
In summary, if you MUST poll or write infinite loops, call Sleep(1) to
yield back to idle and keep CPU utilization under control. If you can
afford to sleep longer, do it. Otherwise if your process creates
windows use WaitForSingleObject or MsgWaitForMultipleObjects.
There was a particularly bad example of a program a few days back, wherein the programmer
used PeekNamedPipe to poll for pipe output. I rewrote it and got a nice essay out of it
showing that you should really use threads, and threads that block; I just posted it
http://www.flounder.com/console_threads.htm
The problem with gratuitous uses of Sleep(1) (==Sleep(15)) is that if there really is
nothing to do, the CPU *could* have been running on your app, but it isn't. So while you
get the illusion of "better performance" the performance is actually significantly worse
as far as your app is concerned, because you may be running orders of magnitude slower
than necessary.
Load management is a subtle issue, and throwing gratuitous Sleep() calls around is another
instance of what I call "pixie dust", which is you sprinkle them around until you see the
effect you want, even if it isn't the right solution to the problem.
joe
****
Joseph M. Newcomer [MVP]
email: newcomer@xxxxxxxxxxxx
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
.
- References:
- Why INFINITE loop in a thread occupy so much CPU time??
- From: lostlander
- Re: Why INFINITE loop in a thread occupy so much CPU time??
- From: Doug Harrison [MVP]
- Re: Why INFINITE loop in a thread occupy so much CPU time??
- From: Ian Semmel
- Re: Why INFINITE loop in a thread occupy so much CPU time??
- From: Geoff
- Why INFINITE loop in a thread occupy so much CPU time??
- Prev by Date: Re: How to access I/O port directly in VC6.0?
- Next by Date: Re: Steps in displaying a UTF-16 BMP value using TextOutW() ???
- Previous by thread: Re: Why INFINITE loop in a thread occupy so much CPU time??
- Next by thread: Re: Why INFINITE loop in a thread occupy so much CPU time??
- Index(es):
Relevant Pages
|