views:

108

answers:

5

To my understanding a Thread.Sleep(0) force a context switch on the OS.

I wanted to check what was the maximum amount of time that could pass in an application before to receive some CPU time.

So I built an application that does Thread.Sleep(0) in a while loop (c#) and calculate the time that pass between each call.

When this application is the only one running on a two core test PC the maximum observed time is right under 1 millisecond (with an average of 0.9 microsecond) and it use all the CPU available (100%).

When I run it along a CPU Filling dummy application (all with the same priority) the max time is around 25ms and the average time is 20ms. It behaves exactly like I expect it. And the time is very stable.

Whenever it gets some CPU time it immediately give the control back to whoever have some processing to do, it's like the hot potato game (CPU usage drops to 0%). If theres no other application running then the control comes back immediately.

Given this behavior I expected this application to have a minimal impact on a computer running real life application. (And to give me the actual "latency" I could expect to see in the applications running there). But to my surprise it did affect negatively (in an observable way) the performance of this specific system.

Am I missing some important point concerning Thread.Sleep(0)?

As a reference here's the code of this application

    private bool _running = true;
    private readonly Stopwatch _timer = new Stopwatch();

    private double _maxTime;
    private long _count;
    private double _average;
    private double _current;

    public Form1()
    {
        InitializeComponent();
        Thread t = new Thread(Run);
        t.Start();
    }

    public void Run()
    {
        while(_running)
        {
            _timer.Start();
            Thread.Sleep(0);
            _timer.Stop();

            _current = _timer.Elapsed.TotalMilliseconds;
            _timer.Reset();
            _count++;

            _average = _average*((_count - 1.0)/_count) + _current*(1.0/_count);
            if(_current>_maxTime)
            {
                _maxTime = _current;
            }
        }
    }

Edited for clarity (purpose of the application): I am currently running a soft real-time multi-threaded application (well, group of applications) that needs to react to some inputs every roughly 300ms but we do miss some deadlines from time to time (less then 1% of the time) and I'm currently trying to improve that number.

I wanted to verify what is the current variability caused by other process on the same machine: I tough that by fitting the application written above on this semi real-time machine the maximum time observed would tell me what variability is caused by the system. I.E. I have 300ms but max observed time before a thread gets some CPU time is standing at 50ms, so to improve the performance I should set my processing time to a maximum of 250ms (since I might already be 50ms late).

+3  A: 

It doesn't force a context switch, only Sleep(1) does that. But if there's any other thread from any process ready to run and has a higher priority then Sleep(0) will yield the processor and let it run. You can see this by running an endless loop that calls Sleep(0), it will burn 100% CPU cycles on one core. I don't understand why you don't observe this behavior.

The best way to keep the system responsive is by giving your thread a low priority.

Hans Passant
Here's a link to Joe Duffy's blog post about it.http://www.bluebytesoftware.com/blog/2006/08/23/PriorityinducedStarvationWhySleep1IsBetterThanSleep0AndTheWindowsBalanceSetManager.aspx
Eric
@Hans : on what basis do you base this statement "It doesn't force a context switch, only Sleep(1) does that"
apoorv020
The SDK documentation.
Hans Passant
@Eric: you could/should write an answer around that link.
Henk Holterman
I might have written that badly initially (and I edited for clarity) but when its the only application running, it does use all the CPU available. Whenever some other applications are running (at the same priority level) the CPU usage immediately drops to 0%. In my mind this application should only use the idle CPU time. (or be invoked for a very short period of time when other thread quantums expire).
Benoittr
@hans : If two applications are running at the same priority and one is waiting to do some processing, then thread.sleep(0) does a context switch right? (If true, I don't really "need" a context switch for the purpose mentionned above... I think?)
Benoittr
"Same priority" doesn't really mean anything. The load balancer is constantly tweaking the effective priority of a thread. You get idle time execution by setting your thread's priority low.
Hans Passant
Saw your edit. To get a reasonable guarantee that your code runs in time you need a *high* priority thread that *blocks* on a waitable event. An event that's signaled causes the scheduler to re-evaluate the thread that should run next. The high priority ensures that it is *your* thread. Do as little as possible, block quickly again. Using Sleep() is about the worst thing you could do, the scheduler has no reason to favor your thread. Missing the deadline once in a while is unavoidable.
Hans Passant
If the OS has no reason to favor a thread over another isn't there a FIFO priority? The application I detailed above really is a measuring tool (trying to figure out how long I should expect my other normal-priority applications to wait before to get some CPU time).In this type of application CPU usage is not whats important, but the latency is, and I'm trying to figure a way to measure it: What is my maximum thread wait time on a given period (before a thread wake up).None of the other applications have a thread.sleep, they have some processing to do when some network activity occur.
Benoittr
LIFO, never FIFO. It's called "round-robin scheduling". The last Windows operating system that supported that strongly was Windows 98 (skipping ME). Written to support DOS programs that polled instead of asking the operating system to wake them up for an available resource. You've written a polling program. Still supported by the scheduler, it takes pity on a thread that hasn't managed to gain the CPU for a while. Boosting its priority, eventually. Using WaitForSingleObjectEx() or WaitHandle.WaitOne() is essential.
Hans Passant
I realize I need a lot more reading on these topic, but you pointed me in the right direction (so I accepted the answer). Thanks for your support.
Benoittr
A: 

I suspect that you noticed impact due to your other program was also only CPU bound. Therefore the is also a third program running your not taking into account the OS scheduler.

The non-impacted program is getting stopped by the OS to allow your program to run, and there is context hit for swapping out the process and then loading yours then unloading it again.

Simeon Pilgrim
+2  A: 

Most likely cause is that you aren't allowing the program to read instructions in an efficient manner.

When you invoke Sleep(0) your code is suspended for a single cycle and then scheduled for the next available slot. However, this context switching isn't free - there are plenty of registers to save/load, and when you have a different sequence of instructions to read from disk you probably end up with quite a few cache misses. I can't imagine this having a significant impact on your application in most cases, but if you are working with a real-time system or something similarly intensive then it might be a possibility.

MikeD
Is there any way to measure how long context switch take?How many registers are saved/loaded when context switch occurs? (I know I can tell how many occurence there is using perfmon)That could maybe explain the performance degradation I see on the system : at the end of every timeslice the OS check if theres some other thread that need to do something, if it find one, it does a context switch, otherwise the processing continue naturally. The little application written above is always in queue for some CPU time thus every time a timeslice ends im forcing a context switch (that shouldn't be)
Benoittr
I'm actually not sure how you would go about measuring a context switch time. The closest solution I know of is a benchmark called LMBench (for Linux) and it might be a good starting point but it probably doesn't have a cross platform port, given the nature of the task.
MikeD
A: 

My understanding is that Thread.Sleep(0) does not force a thread context switch, it simply signals the task scheduler that you are willing to give up the rest of your time slice if there are other threads waiting to execute.

Your loop around Sleep(0) is chewing up CPU time, and that will have a negative effect on other applications (and laptop battery life!). Sleep(0) doesn't mean "let everything else execute first", so your loop will be competing for execution time with other processes.

Passing a non-zero wait time to Sleep() would be marginally better for other apps because it would actually force this thread to be put aside for a minimum amount of time. But this is still not how you implement a minimum-impact background thread.

The best way to run a CPU bound background thread with minimum impact to foreground applications is to lower your thread priority to something below normal. This will tell the scheduler to execute all normal priority threads first, and if/when there is any other time available then execute your low priority thread. The side effect of this is that sometimes your low priority thread may not get any execution time at all for relatively long periods of time (seconds) depending on how saturated the CPU is.

dthorpe
I edited the answer so that the purpose of the application is better understood. But if I lower the priority it wont give me the values I am searching for.
Benoittr
And does it "really" have a negative effect on other applications (since im handing them over my time slice as soon as they have something to do?)
Benoittr
Yes, you're handing them the rest of your time slice, but you're doing that so frequently/so rapidly that you're probably chewing up more time in task switching than in executing app code. Task switching is not a trivial operation - it takes hundreds or thousands of CPU clock cycles to store all the registers and execution context of one thread and load all the context of the other thread. Your thread is not idle, it's spinning in a tight loop and forcing the task scheduler to switch contexts faster than it normally would.
dthorpe
+1  A: 

I was bitten by this bug in a previous project. I had a thread running which would check messages in a prioritized queue, looking for a new one. If it found no new message, I wanted the thread to go to sleep until a message's being added to the queue would wake it back up to check again.

Naively, assuming that Thread.Sleep(0) would cause the thread to go to sleep until woken up again, I found our app consuming crazy amounts of CPU once messages started coming in.

After a few days of sleuthing possible causes, we found the info from this link. The quick fix was to use Thread.Sleep(1). The link has the details around the reason for the difference, including a little test app at the bottom, demonstrating what happens to performance between the 2 options.

Eric