tags:

views:

327

answers:

4

I'm making an app that needs some pretty tight timing and the Stopwatch class is the perfect solution. However, I noticed sometimes, when running on a small panel PC, the Stopwatch values veer way off. I added some debug printouts that monitor the value of the stopwatch every 200 ms or so:

0:00:197
0:00:502
0:00:702
...
0:03:356
0:12:93

0:13:21
0:13:421
...

How could it possibly jump from ~3 seconds to ~13 seconds? I now see that the underlying function QueryPerformanceCounter() is buggy (http://www.virtualdub.org/blog/pivot/entry.php?id=106), but I get the sense that something else is going on here.

Any insight is appreciated.

Update:

Here's a little more detail on my code: it's pretty straightforward. It's a WPF app that creates a new Stopwatch object on startup, then kicks it off via Start(). I then create a DispatcherTimer like so:

displayTimer = new DispatcherTimer(); 
displayTimer.Tick += display_Tick; 
displayTimer.Interval = DISPLAY_INTERVAL_TIMESPAN; 

where the timespan is 200 ms. My debug code simply prints out the value of the Stopwatch object every time the dispatchTimer ticks.

Here is a link to a debug log: dl.dropbox.com/u/7999907/hburg.txt. The first bit of data on each line is the current date/time via DateTime.UtcNow. The time all the way to the right is the value via Stopwatch.Elapsed. You can see how the stopwatch jumps but the DateTime doesn't.

Update2:

Here's a fun Microsoft support article: http://support.microsoft.com/kb/274323

+8  A: 

Exact timing on PC hardware isn't straight forward, period. Here are some resources, showing some experiences with timing in windows, which will be the underlying implementation of any counter in a Windows environment, including .NET:

It explains why sometimes you will get 10ms resolution, depending on which system call you use, and sheds a little more light on why QueryPerformanceCounter is "buggy". One of the points made is that power saving modes/variable CPU speeds can interfere with these timings.

A related concept to this is "locking the timestep" in real-time physical simulations. If you google for that, you may get some ideas on how to work around problems you are having. The basic concept is that you have fixed time steps, and do a sort of producer/consumer implementation for your timing/update functions. I don't know if this applies to your specific domain, but it is considered a best-practices in video games.

Merlyn Morgan-Graham
10 ms resolution is fine for me... I'm seeing a 10 SECOND jump forward by the Stopwatch class.
Tim
I agree... 10 seconds is pretty nuts.
rockinthesixstring
Maybe, just maybe, 10 seconds actually passed? What was your program actually doing?
Daniel Rasmussen
@Tim: This is the closest most people are going to get at guessing your problem. You'll have to show some minimal code that repros the problem, and give specifics about your environment for us to stop guessing :)
Merlyn Morgan-Graham
Ten seconds may be "pretty nuts" but it's very possible if the GC kicks in.
Matthew Whited
+5  A: 
0xA3
Understood, but the dispatcherTimer is simply monitoring the Stopwatch. The dispatcherTimer tick doesn't really need to be accurate for me... I just can't absorb a 10 second jump in my stopwatch :)
Tim
@Tim: Ok, hadn't seen your log file yet when I answered. Are you able to produce a small sample application to reproduce the artifact? By the way, you are able to edit your question and include additional information.
0xA3
Interesting. Unfortunately, I'm running on a small panel PC (with Windows Embedded Standard) that only has one CPU. I'm not very familiar with start.exe, but there is no AFFINITY switch available for me.
Tim
@Tim: Alright, this is actually an important fact that you are observing this on an embedded system and not a regular desktop PC. As the clock is based on hardware after all, this makes a hardware bug the most likely.
0xA3
That's what I was afraid of. I suppose I can roll my own stopwatch that doesn't use 'QueryPerformanceCounter'. It is especially frustrating when a library call doesn't work as advertised. I mean, I can deal with my stuff not working, but not somebody else's :)
Tim
+2  A: 

Perhaps I am misunderstanding your question, but do you need to just measure time intervals or do you need to execute code at those intervals? If you need to execute code, then check out System.Timers.Timer class as it is thread safe and should work fine for multi-processors.

jle
Both :) I need to fire up a method every so often and I need to measure time intervals.
Tim
A: 

I have the same problem in my code, except much worse. I am running a thread that runs some real-time tests continually for 3 hours. I have set period to 3*60*60*1000 milliseconds, and usually this works just fine. However, the other day, the same code ran, and went 3 hours and 24 MINUTES. How could this happen? The measurement code could not take more than a second or so if it entirely failed and timed-out -- it can't explain 24 minutes! Is it possible the timer is synced with the BIOS timer, and it synchronized to a time server, which reset the time by 24 minutes?

        period = 10800000;

        Stopwatch sw1 = new Stopwatch();
        sw1.Start();
        while (sw1.Elapsed.TotalMilliseconds < period && ! bgw.CancellationPending)
        {
           // measurement stuff goes here, typically a total of 300 ms or so
           Thread.Sleep(100);
        }

total elapsed time: 03:24:00.2271363 (measured by an external clock)

Mark Lakata