Environment.TickCount vs DateTime.Now
Asked Answered
F

14

74

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");
Flaxseed answered 28/10, 2008 at 13:40 Comment(6)
As an unrelated aside, if you DO use DateTime for Date-related math calculations, always use DateTime.UtcNow as DateTime.Now is susceptible to Daylight Savings Time...your calculations could be off by a hour, or worse, negative numbers.Shanley
@Scott: Thought it's worth mentioning: even with UtcNow, there is the issue of scheduled NTP synchronizations: not that rarely, system time gets changed by an order of 10 seconds after these updates (on my PC).Bimanous
The math is actually not that complicated... int duration = unchecked((int)((uint)Environment.TickCount - (uint)start)); ...will give you the correct answer regardless of rollovers. (You can ideally skip the cast to back to int unless you absolutely need it.)Fitzgerald
@AnorZaken: you actually don't need any of that casting, as explained elsewhere on this page.Typescript
@GlennSlayden You are correct - my uint casts are pointless and since I cast back to int it doesn't make any sense anyway. Good catch. (Also having the cast back to int inside the unchecked context was a questionable choice on my end.)Fitzgerald
As noted, its granularity is 16.6666 ms (reducing the accuracy to 8.3333 ms on average), so its value always increases by 16 or by 17 (when it increases). That could be a gotcha!!!Photoluminescence
S
74

Use the Stopwatch class. There is a decent example on MSDN: Stopwatch Class

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;
Sitar answered 28/10, 2008 at 13:41 Comment(4)
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.Superheat
In earlier versions you'd use p/invoke-calls marked safe and msdn.microsoft.com/en-us/library/ms901807.aspx, not TickCount.Rounce
If the hardware your program is running on lacks a high resolution timer, then Stopwatch is just as dangerous as DateTime. See connect.microsoft.com/VisualStudio/feedback/details/741848/… as well as Joel's answer below.Tarver
I couldn't find Stopwatch for Portable Class Library so you have to use some workaround.Valleau
H
106

Environment.TickCount is based on GetTickCount() WinAPI function. It's in milliseconds. But the actual precision of it is about 15.6 ms. So you can't measure shorter time intervals (or you'll get 0).

Note: The returned value is Int32, so this counter rolls over each ~49.7 days. You shouldn't use it to measure such long intervals.

DateTime.Ticks is based on GetSystemTimeAsFileTime() WinAPI function. It's in 100s nanoseconds (tenths of microsoconds). The actual precision of DateTime.Ticks depends on the system. On Windows XP, the increment of system clock is about 15.6 ms, the same as in Environment.TickCount. On Windows 7 its precision is 1 ms (while Environemnt.TickCount's is still 15.6 ms). However, if a power saving scheme is used (usually on laptops) it can go down to 15.6 ms as well.

Stopwatch is based on QueryPerformanceCounter() WinAPI function (but if high-resolution performance counter is not supported by your system, DateTime.Ticks is used)

Before using StopWatch notice two problems:

  • it can be unreliable on multiprocessor systems (see MS kb895980, kb896256)
  • it can be unreliable if CPU frequency varies (read this article)

You can evaluate the precision on your system with simple test:

static void Main(string[] args)
{
    int xcnt = 0;
    long xdelta, xstart;
    xstart = DateTime.UtcNow.Ticks;
    do {
        xdelta = DateTime.UtcNow.Ticks - xstart;
        xcnt++;
    } while (xdelta == 0);

    Console.WriteLine("DateTime:\t{0} ms, in {1} cycles", xdelta / (10000.0), xcnt);

    int ycnt = 0, ystart;
    long ydelta;
    ystart = Environment.TickCount;
    do {
        ydelta = Environment.TickCount - ystart;
        ycnt++;
    } while (ydelta == 0);

    Console.WriteLine("Environment:\t{0} ms, in {1} cycles ", ydelta, ycnt);


    Stopwatch sw = new Stopwatch();
    int zcnt = 0;
    long zstart, zdelta;

    sw.Start();
    zstart = sw.ElapsedTicks; // This minimizes the difference (opposed to just using 0)
    do {
        zdelta = sw.ElapsedTicks - zstart;
        zcnt++;
    } while (zdelta == 0);
    sw.Stop();

    Console.WriteLine("StopWatch:\t{0} ms, in {1} cycles", (zdelta * 1000.0) / Stopwatch.Frequency, zcnt);
    Console.ReadKey();
}
Hodgepodge answered 14/1, 2012 at 21:42 Comment(8)
Just a note for your loop - instead of a for loop, you should use Thread.SpinWait, this will make sure the loop isn't optimized away (you're specifying that the "waste" loop is what you want semantically). Other than that, it should be more or less equivallent to the for cycle.Pannikin
@Pannikin : thanks, I have changed the sample code to show the precision.Hodgepodge
Very detailed explanation. On my win10 desktop, the results are similar to your Win7 report. StopWatch looks like high precision. DateTime: 1,0278 ms, in 21272 cycles Environment: 16 ms, in 4786363 cycles StopWatch: 0,00087671156014329 ms, in 1 cyclesBarbi
A minor fix on your excellent code: since Stopwatch is fully passive (in the sense that it never runs any code on its own), your call to sw.Stop() is pointless, causing only a flurry of wasted Win32 calls. Normally the issue wouldn't be worth a mention, but here it draws attention what might seem counter-intuitive to some—namely, that Stopwatch is non-active (also not IDisposable) and can be summarily abandoned at any time. The offended "intuition" is that something has been "left running," but the insight might simplify system design around (e.g.) shutdown, typically a complex area.Typescript
@GlennSlayden, you are correct. I believe it's a rudiment of a very first version of code that was printing sw.ElapsedTicks to stdout after sw.Stop()Hodgepodge
QueryPerformanceCounter() is reliable nowadays: see blogs.msdn.microsoft.com/oldnewthing/20140822-01/?p=163 and msdn.microsoft.com/en-us/library/windows/desktop/…: "Q: Does QPC reliably work on multi-processor systems, multi-core system, and systems with hyper-threading? A: Yes", "Q: Is QPC accuracy affected by processor frequency changes caused by power management or Turbo Boost technology? A: No"Stroh
Your "problems" noted are essentially irrelevant nowadays. They apply to Windows 2000, Windows 2003, and Windows XP. If you're running those systems, you have bigger problems than whether your timers are accurate.Rna
Re "It's in milliseconds. But the actual precision of it is about 15.6 ms": Don't you mean 16.6 ms (corresponding to the 60 Hz PC tick counter)? (There are three instances of 15.6 ms.)Photoluminescence
S
74

Use the Stopwatch class. There is a decent example on MSDN: Stopwatch Class

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;
Sitar answered 28/10, 2008 at 13:41 Comment(4)
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.Superheat
In earlier versions you'd use p/invoke-calls marked safe and msdn.microsoft.com/en-us/library/ms901807.aspx, not TickCount.Rounce
If the hardware your program is running on lacks a high resolution timer, then Stopwatch is just as dangerous as DateTime. See connect.microsoft.com/VisualStudio/feedback/details/741848/… as well as Joel's answer below.Tarver
I couldn't find Stopwatch for Portable Class Library so you have to use some workaround.Valleau
C
30

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, lets 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.

Costello answered 3/7, 2009 at 6:59 Comment(3)
Best answer IMO. This will not "explode after 50 days" either, unless you are trying to use it to track a period of time that is longer than 50 days, which seems very unlikely. +1 for including solid proof of this working in an easily testable way. StopWatch is a useless class, due to the issues with multi-core CPUs which is pretty much every PC these days.Kathleenkathlene
Note: This example will work only when the code is built without check for arithmetic overflow/underflow (it's turned off by default). If you turn it on (Project Settings > Build > Advanced... > Check for arithmetic overflow/underflow) the code will raise System.OverflowException : Arithmetic operation resulted in an overflowStroh
@Kathleenkathlene StopWatch is reliable nowadays: see #243851Stroh
T
11

Environment.TickCount seems to be much faster than the other solutions:

Time function           Time   Time per
                        [ms]   call [ns]
----------------------------------------
Environment.TickCount     71     7.1
DateTime.UtcNow.Ticks    213    21.3
sw.ElapsedMilliseconds  1273   127.3

The measurements were generated by the following code:

static void Main(string[] args) {
    const int max = 10000000;
    //
    //
    for (int j = 0; j < 3; j++) {
        var sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < max; i++) {
            var a = Environment.TickCount;
        }
        sw.Stop();
        Console.WriteLine($"Environment.TickCount {sw.ElapsedMilliseconds}");
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < max; i++) {
            var a = DateTime.UtcNow.Ticks;
        }
        sw.Stop();
        Console.WriteLine($"DateTime.UtcNow.Ticks {sw.ElapsedMilliseconds}");
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < max; i++) {
            var a = sw.ElapsedMilliseconds;
        }
        sw.Stop();
        Console.WriteLine($"sw.ElapsedMilliseconds {sw.ElapsedMilliseconds}");
    }
    Console.WriteLine("Done");
    Console.ReadKey();
}
Treatment answered 11/4, 2017 at 6:43 Comment(6)
+1 from me, because this is, what I wanted to find out. If someone is reading this: Why this answer has no comments, and no other up/downvotes?? (as of 5/2017) If something is not correct, please comment, if you think, this is correct, please upvote.Prefab
I have extended the benchmark, see my answer. BTW, there are interesting differences, if you execute these benchmarks in release or debug mode (The latter you probably shouldn't do for 'real' results, but it is interesting to see, which tests get really optimized by the compiler and which not).Prefab
Re "seems to be much faster": In terms of how much it costs in time to call it, presumably? In absolute terms, calling ElapsedMilliseconds() costs about 130 ns. Whether that is significant or not depends on what it is used for. Calling TickCount() can probably be considered cheap (about 7 nanoseconds).Photoluminescence
The loop overhead is not accounted for (being in TickCount()'s favor, both in relative and absolute terms).Photoluminescence
Any benchmark, even if it is only for relative performance differences, ought to include on what platform it was run on: Hardware (CPU, clock frequency, any varying clock frequency scheme (like AMD's "Core Performance Boost"; e.g., if it is enabled in the BIOS or not)), size of L1, L2, and L3 caches, etc.), operating system (incl. version and edition), .NET version (incl. patch level), etc.Photoluminescence
cont' - And (Visual Studio) build options (e.g., release / debug).Photoluminescence
P
11

Here is kind of an updated&refreshed summary of what may be the most useful answers and comments in this thread. Plus extra benchmarks and variants:

First thing first: As others have pointed out in comments, things have changed the last years and with "modern" Windows (Windows XP and later) and .NET, and modern hardware there are no or little reasons not to use Stopwatch(). See MSDN for details. Quotations:

"Is QPC accuracy affected by processor frequency changes caused by power management or Turbo Boost technology?
No. If the processor has an invariant TSC, the QPC is not affected by these sort of changes. If the processor doesn't have an invariant TSC, QPC will revert to a platform hardware timer that won't be affected by processor frequency changes or Turbo Boost technology.

Does QPC reliably work on multi-processor systems, multi-core system, and systems with hyper-threading?
Yes

How do I determine and validate that QPC works on my machine?
You don't need to perform such checks.

Which processors have non-invariant TSCs?
[..Read further..] "

But if you don't need the precision of Stopwatch() or at least want to know exactly about the performance of Stopwatch (static vs. instance-based) and other possible variants, continue reading:

I took over the benchmark from cskwg, and extended the code for more variants. I have measured with a some years old Core i7 i7-4700MQ ("Haswell-MB" (quad-core, 22 nm). 2013 vintage) and C# 7 with Visual Studio 2017 (to be more precise, compiled with .NET 4.5.2, despite binary literals, it is C# 6 (used of this: string literals and 'using static'). Especially the Stopwatch() performance seems to be improved compared to the mentioned benchmark.

This is an example of results of 10 million repetitions in a loop. As always, absolute values are not important, but even the relative values may differ on other hardware:

32 bit, Release mode without optimization:

Measured: GetTickCount64() [ms]: 275
Measured: Environment.TickCount [ms]: 45
Measured: DateTime.UtcNow.Ticks [ms]: 167
Measured: Stopwatch: .ElapsedTicks [ms]: 277
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 548
Measured: static Stopwatch.GetTimestamp [ms]: 193
Measured: Stopwatch+conversion to DateTime [ms]: 551
Compare that with DateTime.Now.Ticks [ms]: 9010

32 bit, Release mode, optimized:

Measured: GetTickCount64() [ms]: 198
Measured: Environment.TickCount [ms]: 39
Measured: DateTime.UtcNow.Ticks [ms]: 66 (!)
Measured: Stopwatch: .ElapsedTicks [ms]: 175
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 491
Measured: static Stopwatch.GetTimestamp [ms]: 175
Measured: Stopwatch+conversion to DateTime [ms]: 510
Compare that with DateTime.Now.Ticks [ms]: 8460

64 bit, Release mode without optimization:

Measured: GetTickCount64() [ms]: 205
Measured: Environment.TickCount [ms]: 39
Measured: DateTime.UtcNow.Ticks [ms]: 127
Measured: Stopwatch: .ElapsedTicks [ms]: 209
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 285
Measured: static Stopwatch.GetTimestamp [ms]: 187
Measured: Stopwatch+conversion to DateTime [ms]: 319
Compare that with DateTime.Now.Ticks [ms]: 3040

64 bit, Release mode, optimized:

Measured: GetTickCount64() [ms]: 148
Measured: Environment.TickCount [ms]: 31 (is it still worth it?)
Measured: DateTime.UtcNow.Ticks [ms]: 76 (!)
Measured: Stopwatch: .ElapsedTicks [ms]: 178
Measured: Stopwatch: .ElapsedMilliseconds [ms]: 226
Measured: static Stopwatch.GetTimestamp [ms]: 175
Measured: Stopwatch+conversion to DateTime [ms]: 246
Compare that with DateTime.Now.Ticks [ms]: 3020

It may be very interesting, that creating a DateTime value to print out the Stopwatch time seems to have nearly no costs. Interesting in a more academic than practical way is that static Stopwatch is slightly faster (as expected). Some optimization points are quite interesting. For example, I cannot explain why Stopwatch.ElapsedMilliseconds only with 32 bit is so slow compared to it's other variants, for example the static one. This and DateTime.Now more than double their speed with 64 bit.

You can see: Only for millions of executions, the time of Stopwatch begins to matter. If this is really the case (but beware micro-optimizing too early), it may be interesting that with GetTickCount64(), but especially with DateTime.UtcNow, you have a 64 bit (long) timer with less precision than Stopwatch, but faster, so that you don't have to mess around with the 32 bit "ugly" Environment.TickCount.

As expected, DateTime.Now is by far the slowest of all.

If you run it, the code retrieves also your current Stopwatch accuracy and more.

Here is the full benchmark code:

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;

[...]

    [DllImport("kernel32.dll") ]
    public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start

    static void Main(string[] args)
    {
        const int max = 10_000_000;
        const int n = 3;
        Stopwatch sw;

        // Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
        // But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        Thread.Sleep(2); // warmup

        Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
        for (int j = 0; j < n; j++)
        {
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = GetTickCount64();
            }
            sw.Stop();
            Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
            }
            sw.Stop();
            Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = DateTime.UtcNow.Ticks;
            }
            sw.Stop();
            Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = sw.ElapsedMilliseconds;
            }
            sw.Stop();
            Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = Stopwatch.GetTimestamp();
            }
            sw.Stop();
            Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            DateTime dt=DateTime.MinValue; // just init
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
            }
            sw.Stop();
            //Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
            Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]:  {sw.ElapsedMilliseconds}");

            Console.WriteLine();
        }
        //
        //
        sw = new Stopwatch();
        var tickCounterStart = Environment.TickCount;
        sw.Start();
        for (int i = 0; i < max/10; i++)
        {
            var a = DateTime.Now.Ticks;
        }
        sw.Stop();
        var tickCounter = Environment.TickCount - tickCounterStart;
        Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");

        Console.WriteLine($"{NewLine}General Stopwatch information:");
        if (Stopwatch.IsHighResolution)
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
        else
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");

        double freq = (double)Stopwatch.Frequency;
        double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
        Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
        Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");

        DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L);  // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
        Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
        // this conversion from seems not really accurate, it will be between 24-25 days.
        Console.WriteLine($"{NewLine}Done.");

        while (Console.KeyAvailable)
            Console.ReadKey(false);
        Console.ReadKey();
    }
Prefab answered 3/5, 2017 at 22:6 Comment(1)
Re "absolute values are not important": They are as input to a decision about whether 70 nanoseconds spend on calling a function is significant or not. These functions don't take milliseconds to execute.Photoluminescence
C
10

You probably want System.Diagnostics.StopWatch.

Corned answered 28/10, 2008 at 13:41 Comment(0)
R
9

If you're looking for the functionality of Environment.TickCount but without the overhead of creating new Stopwatch objects, you can use the static Stopwatch.GetTimestamp() method (along with Stopwatch.Frequency) to calculate long time spans. Because GetTimestamp() returns a long, it won't overflow for a very, very long time (over 100,000 years, on the machine I'm using to write this). It's also much more accurate than Environment.TickCount which has a maximum resolution of 10 to 16 milliseconds.

Rna answered 10/6, 2011 at 15:37 Comment(3)
Unfortunately Stopwatch.GetTimestamp() isn't as convenient as Environment.TickCount because it returns some abstract ticks rather than ordinary time unitsStroh
If there are high performance considerations, one should note that Environment.TickCount is considerably faster. Benchmarks here (blog.tedd.no/2015/08/17/…) show the following relative times: Environment.TickCount: 7ms, DateTime.Now.Ticks: 1392ms, Stopwatch.ElapsedMilliseconds: 933ms.Hyperplasia
@SpecialSauce but note that they're not equivalent things. Also note that on my system, Stopwatch.ElapsedMilliseconds is ~35 times faster than his system, while my Environment.TickCount is only ~2 times faster. Your hardware may (apparently) vary.Rna
T
8

Use

System.Diagnostics.Stopwatch

It has a property called

EllapsedMilliseconds
Torrlow answered 28/10, 2008 at 13:42 Comment(0)
T
3

TickCount64

Doing some quick measurements on this new function, I found (optimized, release 64-bit, 1000 million loops):

Time function             Time   Time per
                          [ms]   call [ns]
------------------------------------------
Environment.TickCount    2265     2.265
Environment.TickCount64  2531     2.531
DateTime.UtcNow.Ticks   69016    69.016

The measurements for not-optimized code were similar. Test code:

static void Main( string[] args ) {
    long start, end, length = 1000 * 1000 * 1000;
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = Environment.TickCount;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "Environment.TickCount: {0}", end - start );
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = Environment.TickCount64;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "Environment.TickCount64: {0}", end - start );
    start = Environment.TickCount64;
    for ( int i = 0; i < length; i++ ) {
        var a = DateTime.UtcNow.Ticks;
    }
    end = Environment.TickCount64;
    Console.WriteLine( "DateTime.UtcNow.Ticks: {0}", end - start );
}
Treatment answered 13/7, 2020 at 6:36 Comment(2)
Any benchmark, even if it is only for relative performance differences, ought to include on what platform it was run on: Hardware (CPU, clock frequency, any varying clock frequency scheme (like AMD's "Core Performance Boost"; e.g., if it is enabled in the BIOS or not)), size of L1, L2, and L3 caches, etc.), operating system (incl. version and edition), .NET version (incl. patch level), C# version, Visual Studio version, etc.Photoluminescence
The loop overhead also ought to be accounted for (incl. preventing the compiler from optimising an empty loop out).Photoluminescence
B
0

You should use the Stopwatch class instead.

Bolivia answered 28/10, 2008 at 13:42 Comment(0)
A
0

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.

Alexandrina answered 28/10, 2008 at 14:23 Comment(8)
#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.Gossett
#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).Oehsen
@Oehsen 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.Alexandrina
I was just being hit by the wrap around when my algorithm was hanging. This PC was running/hibernating for > 24 days. How nasty!Ruskin
@usr: that's your own fault, for using TickCount to measure such a long time span. :)Alexandrina
Sure was! I wanted to share so other will not be bitten by this.Ruskin
Wrapping after 50 days is not good on a server. Hope you are not on-call when it happens.Ruskin
As noted, its granularity is 16.6666 ms (reducing the accuracy to 8.3333 ms on average), so its value always increases by 16 or by 17 (when it increases). That could be a gotcha!!!Photoluminescence
E
0

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.

Eyewitness answered 28/10, 2008 at 19:34 Comment(1)
Re "how expensive it can be getting the time on some systems": Yes, that is what tick counters are for (probably a very thin layer over a (hardware) timer).Photoluminescence
V
0

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.

Vitrify answered 17/3, 2009 at 0:25 Comment(0)
T
0

Overflow compensation

As said before, rollover may happen after 24.9 days, or, if you use an uint cast, after 49.8 days. Because I did not want to P/Invoke GetTickCount64, I wrote this overflow compensation. The sample code is using 'byte' to keep the numbers handy. Please have a look at it; it still might contain errors:

using System;
namespace ConsoleApp1 {
    class Program {
        //
        static byte Lower = byte.MaxValue / 3;
        static byte Upper = 2 * byte.MaxValue / 3;
        //
        ///<summary>Compute delta between two TickCount values reliably, because TickCount might wrap after 49.8 days.</summary>
        static short Delta( byte next, byte ticks ) {
            if ( next < Lower ) {
                if ( ticks > Upper ) {
                    return (short) ( ticks - ( byte.MaxValue + 1 + next ) );
                }
            }
            if ( next > Upper ) {
                if ( ticks < Lower ) {
                    return (short) ( ( ticks + byte.MaxValue + 1 ) - next );
                }
            }
            return (short) ( ticks - next );
        }
        //
        static void Main( string[] args ) {
            // Init
            Random rnd = new Random();
            int max = 0;
            byte last = 0;
            byte wait = 3;
            byte next = (byte) ( last + wait );
            byte step = 0;
            // Loop tick
            for ( byte tick = 0; true; ) {
                //
                short delta = Delta( next, tick );
                if ( delta >= 0 ) {
                    Console.WriteLine( "RUN: last: {0} next: {1} tick: {2} delta: {3}", last, next, tick, delta );
                    last = tick;
                    next = (byte) ( last + wait );
                }
                // Will overflow to 0 automatically
                step = (byte) rnd.Next( 0, 11 );
                tick += step;
                max++; if ( max > 99999 ) break;
            }
        }
    }
}
Treatment answered 12/7, 2020 at 11:23 Comment(1)
I did not measure and compare performance for this compensation algorithm.Treatment

© 2022 - 2024 — McMap. All rights reserved.