Dela via


Computing Time in .NET

One of the pitfalls with the abundance of classes that .NET provides is not picking the right class for the job. The right selection is especially important when performance is a consideration. In this article, I will use a tool called MeasureIt and walk you through rapidly benchmarking various classes and look at a couple of means of measuring time.

In this post, I will compare the performance of System.DateTime.Now versus System.DateTime.UtcNow – which one is more efficient and by how much? System.DateTime.Now is frequently used in .NET programming as many applications have a need to get time. System.DateTime.Now calculates the local time and returns a DateTime object. But, in many instances, is local time with the overhead for Daylight Savings Time (DST) and Time Zones really needed? (A separate issue is if you use System.DateTime.Now for measuring time differences and DST kicks in, you may find your end times to be “earlier” than the start times!).

System.DateTime.UtcNow provides the Coordinated Universal Time as opposed to the local time – would this be a better bet than System.DateTime.Now if you don’t need local time? For the impatient, our experiments reveal that the performance of DateTime.UtcNow is 30X better than DateTime.Now.

Anytime you want to find out the relative performance of one class versus another, MeasureIt can be a very handy tool. It is well worth your time to find the right class for the job, and using MeasureIt the task of benchmarking is made easy.

MeasureIt

Vance Morrison has written a couple of MSDN articles about MeasureIt (he wrote the tool as well). He calls for measuring performance often, measuring performance early and thinking about performance at design time – amen to that!

The MSDN articles (it is a two part series) can be found at:
https://msdn.microsoft.com/en-us/magazine/cc500596.aspx
https://msdn.microsoft.com/en-us/magazine/cc507639.aspx

You can download MeasureIt at: https://blogs.msdn.com/vancem/attachment/9403324.ashx

Let me quickly summarize the steps needed to get you running:

  1. Click on the 'MeasureIt.zip' link above. This brings up a download dialog box.
  2. Click OK and Open the zip file.
  3. Copy the 'MeasureIt' directory to your hard drive. 
  4. Click on the 'MeasureIt.exe' file
  5. Click on the 'Run' dialog box that comes up.

The detailed user’s guide that comes along with the tool is very useful and I recommend reading it in full.

Type MeasureIt /UsersGuide in a command window to see the user’s guide.
Type MeasureIt /edit to edit the source and add more benchmarks.

Benchmarking Time

We want to measure how DateTime.Now and DateTime.UtcNow compare. Per the nice user’s guide from Vance on MeasureIt, I type MeasureIt.exe /edit in a command prompt, which brings up the MeasureIt solution in Visual Studio (isn’t that really cool?) and add a new “area” called MeasureTime and add the benchmarks for DateTime.Now and DateTime.UtcNow. The code I added to the MeasureIt class in the MeasureIt.cs file is just the following few lines:

  static public void MeasureTime()

    {

        DateTime time;

        timer1000.Measure("DateTime.Now", 1, delegate

        {

            time = DateTime.Now;

        });

        long stime;

        timer1000.Measure("DateTime.UtcNow", 1, delegate

        {

            time = DateTime.UtcNow;

        });

       

     

    }

After adding this code, I ran MeasureIt.exe time in a command prompt to filter out all other benchmarks and I get a nice page that opens in Internet Explorer giving me the results and explaining me how to read it. The results that MeasureIt reports is normalized to EmptyStaticFunction(). In order to convert the results into absolute time, look for the line which informs you what 1.0 unit represents {For, these experiments, MeasureIt indicated the results were Scaled where EmptyStaticFunction = 1.0 (1.5 nsec = 1.0 units) }. Thus, 200 units represents 300ns. The reason that MeasureIt normalizes is to enable comparisons at different times. When the CPU load is low, you may get very low absolute times; when the CPU load is high, you may get larger absolute times. However, the normalization to EmptyStaticFunction ensures that your comparisons are still valid. For our experiment, since we are only looking at one run, the normalization is not that interesting.

Name

Median

Mean

StdDev

Min

Max

Samples

NOTHING [count=1000]

0.000

0.055

0.166

0.000

0.552

10

MethodCalls: EmptyStaticFunction() [count=1000 scale=10.0]

1.000

1.010

0.029

1.000

1.097

10

Loop 1K times [count=1000]

249.310

249.428

0.289

249.310

250.276

10

Time: DateTime.Now [count=1000]

235.000

237.531

8.357

231.586

261.448

10

Time: DateTime.UtcNow [count=1000]

7.517

7.545

0.274

7.379

8.345

10

 

The first three rows are giving you a basis since the benchmarks we are running are very small and will vary run to run dramatically. The first row just gives you a feel for what measurement error you can expect, assuming your benchmark does NOTHING. The [count=1000] tells you that the numbers reported are averaged across 1000 runs (if you do what I did and repeat the benchmark 10 times, that means the numbers are averaged across 10000 runs).

The second row acts as a baseline, with 1.0 unit being equivalent to 1.5 ns. In this run DateTime.Now reported 240 units (360 ns). If during another run, you find that DateTime.Now reported 480 units (720 ns), and MethodCalls takes 2 units (3 ns), you can conclude that it was just the system variability in the two runs that caused the spike and DateTime.Now did not become slower.

The third row (Loop 1K times) is a dummy loop that tries to kick the CPU into action and out of any low power state that it may have been to make measurements as realistic as possible. Finally, MeasureIt also provides the median and mean values along with the standard deviation it observed run to run. About 95% of all measurements fall within two standard deviations of the mean.

Okay, enough with the explanation. The highlighted rows in the table compare DateTime.Now with DateTime.UtcNow.
DateTime.UtcNow is almost 30x faster than DateTime.Now!

Computing local time takes a fair amount of work including getting time zones and setting up DST making it substantially expensive relative to DateTime.UtcNow. Thus, if you don’t want local time, use DateTime.UtcNow.

Conclusion

Using MeasureIt to quickly benchmark different classes that you plan to use in your scenario can be valuable to improving your application’s performance. Calculating time, it turns out is more complex than one would think (having to get time zones, DST, etc. correct). Using the right class for getting time can affect the running time of your scenario significantly if you get time frequently. In conclusion, using DateTime.UtcNow in places where you do not need local time can give your scenario a time boost!