1

Stumbled upon unexpected behaviours during elapsed time measurements.

I've started from Stopwatch class:

for (int i = 0; i < 100; i++)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    Thread.Sleep(200);
    stopwatch.Stop();
    Console.WriteLine(stopwatch.ElapsedMilliseconds);
}

https://dotnetfiddle.net/3hch1R

But it turned out that every once in a while elapsed time will be less than 200ms.

Then was a time of DateTime.Now and it showed something strange as well:

for (int i = 0; i < 100; i++)
{
    var start = DateTime.Now.Millisecond;
    Thread.Sleep(200);
    var stop = DateTime.Now.Millisecond;
    Console.WriteLine(stop - start);    
}

https://dotnetfiddle.net/2vo2yw

Thing as simple as this regularly returns negative results.

So my question is: what is the reason of such behaviour for both classes and how can I measure elapsed time more precisely (without negative deviations) ?
There is a post about stopwatch which shed some light on the topic, but comments there look a bit contradictory.

Edit1:
As AlexD spotted, DateTime.Now.Millisecond simply returns a millisecond part, not TotalMilliseconds. So this one is clear.

Edit2:
Following suggestions, I've decided to go with DateTime, it's results satisfies my precision requirements:

for (int i = 0; i < 100; i++)
{
    var start = (int)DateTime.UtcNow.TimeOfDay.TotalMilliseconds;
    Thread.Sleep(200);
    var stop = (int)DateTime.UtcNow.TimeOfDay.TotalMilliseconds;
    Console.WriteLine(stop - start);    
}

Or DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond if day changing between measurements is a case for you.

Dima
  • 6,721
  • 4
  • 24
  • 43
  • Thread.Sleep is notoriously innacurate, see http://stackoverflow.com/questions/12321225/net-thread-sleep-is-randomly-imprecise – Alex Jul 16 '14 at 23:22
  • @Alex I've initially used `System.Threading.Timer`, which showed the same results, so I've decided to use `Thread.Sleep` in examples for space saving sake – Dima Jul 16 '14 at 23:25
  • Same answer, see here: http://stackoverflow.com/questions/3744032/why-are-net-timers-limited-to-15-ms-resolution . I think it's only sleeping for approximately 200ms, which sometimes means a little less. – Alex Jul 16 '14 at 23:30
  • I cannot reproduce the first problem. For me `stopwatch.ElapsedMilliseconds` returns values `>= 200` so far. – AlexD Jul 16 '14 at 23:39
  • where you trying to get like 200,400,600,...so on? – terrybozzio Jul 16 '14 at 23:40
  • @AlexD it really depends on both hardware and software (like, I have different results on 2 laptops which I have), so you have to believe me – Dima Jul 16 '14 at 23:47
  • A very similar issue: http://stackoverflow.com/questions/1303667/how-accurate-is-thread-sleeptimespan – AlexD Jul 17 '14 at 00:06
  • Just be aware of midnight problem with `TimeOfDay.TotalMilliseconds` :). – AlexD Jul 17 '14 at 01:28
  • @AlexD yeah, sure, it's not the issue for me. Otherwise, it may be changed to `(DateTime.UtcNow.Ticks / TimeSpan.TicksPerMillisecond)` – Dima Jul 17 '14 at 02:09

2 Answers2

2

Ad 1: Thread.Sleep is not a very accurate method to sleep... I suggest you use a combination of sleep and busy wait: sleep for, say, 180ms, then use busy wait until you reach 200ms; this will load the CPU 100% during the last 20ms (average CPU load of 10%, which might be reasonable), but will give you more accurate release time.

Ad 2: Use DateTime.Ticks (http://msdn.microsoft.com/en-us/library/system.datetime.ticks(v=vs.110).aspx), which returns the number of 'ticks' (100ns intervals) that have passed 'since beginning of time'.

kzagar
  • 355
  • 2
  • 4
  • like I said, Thread.Sleep is there only for question space saving. Initially it was System.Theading.Timer. Do you know, does Timer suffers the same problem as Thread.Sleep? – Dima Jul 17 '14 at 01:15
1

But it turned out that every once in a while elapsed time will be less than 200ms.

I cannot reproduce your results (neither locally, nor with the link to dotnetfiddle.net), but anyway Thread.Sleep does not look accurate. See e.g. How accurate is Thread.Sleep(TimeSpan)?.

Maybe a bit off-topic, but for WINAPI Sleep function MSDN says (emphasis mine):

The system clock "ticks" at a constant rate. 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.


Thing as simple as this regularly returns negative results.

DateTime.Now.Millisecond does not return total milliseconds, just milliseconds component.

Community
  • 1
  • 1
AlexD
  • 32,156
  • 3
  • 71
  • 65