views:

3771

answers:

8

Is it ever OK to use Environment.TickCount to calculate time spans?

int start = Environment.TickCount;
// Do stuff
int duration = Environment.TickCount - start;
Console.WriteLine("That took " + duration " ms");

Because TickCount is signed and will rollover after 25 days (it takes 50 days to hit all 32 bits, but you have to scrap the signed bit if you want to make any sense of the math), it seems like it's too risky to be useful.

I'm using DateTime.Now instead. Is this the best way to do this?

DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");
+19  A: 

Use Stopwatch class. There is a decent example on msdn: http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;
Grzenio
The Stopwatch class is only available in .NET Framework 2.0 and upwards. In the older versions you would use the TickCount, possibly combined with a TimeSpan to check if you had crossed the 25 day boundary.
Rune Grimstad
Just curious, how many people still use the 1.1 BCL? Even Mono supports 3.5.
Dykam
+6  A: 

You probably want System.Diagnostics.StopWatch.

Joel Coehoorn
+5  A: 

Use

System.Diagnostics.Stopwatch

It has a property called

EllapsedMilliseconds
Martin Kool
+1  A: 

You should use the Stopwatch class instead.

hmemcpy
+3  A: 

I use Environment.TickCount because:

  1. The Stopwatch class is not in the Compact Framework.
  2. Stopwatch uses the same underlying timing mechanism as TickCount, so the results won't be any more or less accurate.
  3. The wrap-around problem with TickCount is cosmically unlikely to be hit (you'd have to leave your computer running for 27 days and then try to measure a time that just happens to span the wrap-around moment), and even if you did hit it the result would be a huge negative time span (so it would kind of stand out).

That being said, I would also recommend using Stopwatch, if it's available to you. Or you could take about 1 minute and write a Stopwatch-like class that wraps Environment.TickCount.

BTW, I see nothing in the Stopwatch documentation that mentions the wrap-around problem with the underlying timer mechanism, so I wouldn't be surprised at all to find that Stopwatch suffers from the same problem. But again, I wouldn't spend any time worrying about it.

MusiGenesis
Well, at least #1 and #3 are right.
MusiGenesis
#3 really isn't right, Matthews answer has a better explanation. It's not about relative times near the wraparound, the wraparound is only a problem if you _measure_ times longer than the twenty something days.
Freed
#3 is plain wrong even, this is very much an issue if you run anything that could be used as a service on a server, or even applications on a terminal server. Even my home computer sometimes goes on for two or more months without a reboot (not sure how standby and hibernation act on the ticks, but I suspect that they don't reset them).
Lucero
@Lucero and Freed: you definitely should *not* be using `Stopwatch` (or `TickCount`, for that matter) to measure timespans that long, anyway. While highly granular, `Stopwatch` has very poor long-term accuracy, getting off by as much as 5-10 seconds (not *milliseconds*) *per day*.
MusiGenesis
A: 

I was going to say wrap it into a stopwatch class, but Grzenio already said the right thing, so I will give him an uptick. Such encapsulation factors out the decision as to which way is better, and this can change in time. I remember being shocked at how expensive it can be getting the time on some systems, so having one place that can implement the best technique can be very important.

dongilmore
A: 

For one-shot timing, it's even simpler to write

Stopwatch stopWatch = Stopwatch.StartNew();
...dostuff...
Debug.WriteLine(String.Format("It took {0} milliseconds",
                              stopWatch.EllapsedMilliseconds)));

I'd guess the cosmically unlikely wraparound in TickCount is even less of a concern for StopWatch, given that the ElapsedTicks field is a long. On my machine, StopWatch is high resolution, at 2.4e9 ticks per second. Even at that rate, it would take over 121 years to overflow the ticks field. Of course, I don't know what's going on under the covers, so take that with a grain of salt. However, I notice that the documentation for StopWatch doesn't even mention the wraparound issue, while the doc for TickCount does.

vanmelle
+2  A: 

Why are you worried about rollover? As long as the duration you are measuring is under 24.9 days and you calculate the relative duration, you're fine. It doesn't matter how long the system has been running, as long as you only concern yourself with your portion of that running time (as opposed to directly performing less-than or greater-than comparisons on the begin and end points). I.e. this:

 int before_rollover = Int32.MaxValue - 5;
 int after_rollover = Int32.MinValue + 7;
 int duration = after_rollover - before_rollover;
 Console.WriteLine("before_rollover: " + before_rollover.ToString());
 Console.WriteLine("after_rollover: " + after_rollover.ToString());
 Console.WriteLine("duration: " + duration.ToString());

correctly prints:

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

You don't have to worry about the sign bit. C#, like C, let's the CPU handle this.

This is a common situation I've run into before with time counts in embedded systems. I would never compare beforerollover < afterrollover directly, for instance. I would always perform the subtraction to find the duration that takes rollover into account, and then base any other calculations on the duration.