Friday, March 24, 2006 5:14 PM bart

Performance measurement in .NET 2.0 - the birth of Stopwatch

In my post about generics a couple of days ago (see http://community.bartdesmet.net/blogs/bart/archive/2006/03/22/3831.aspx, updated the demo code because of a problem with the performance measurement :O) I've been using the oldfashioned way to do performance measurement in .NET using P/Invoke to the Win32 API. Basically you need to do the following:

  1. Import the following native functions (using System.Runtime.InteropServices;):

       [DllImport("kernel32.dll")]
       internal static extern int QueryPerformanceCounter(out Int64 lpPerformanceCount);

       [DllImport("kernel32.dll")]
       internal static extern int QueryPerformanceFrequency(out Int64 lpPerformanceCount);
  2. Add the following member to the class:

       static decimal GetSecondsElapsed(long start, long stop)
       {
          long queryFrequency;
          QueryPerformanceFrequency(out queryFrequency);
          decimal result = Convert.ToDecimal(stop - start) / Convert.ToDecimal(queryFrequency);
          return Math.Round(result, 6);
       }

  3. Perform testing as follows:

          long begin;
          QueryPerformanceCounter(out begin);

          // code to test for performance


          long end;
          QueryPerformanceCounter(out end);
          decimal seconds = GetSecondsElapsed(begin, end);

This might look a bit overcomplicated and it actually is, so the .NET Framework guys created a new helper class to assist in this kind of work: System.Diagnostics.Stopwatch was born. Here is how to use it:

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.InteropServices;

class Gen
{
   [DllImport("kernel32.dll")]
   internal static extern int QueryPerformanceCounter(out Int64 lpPerformanceCount);

   [DllImport("kernel32.dll")]
   internal static extern int QueryPerformanceFrequency(out Int64 lpPerformanceCount);

   public static void Main()
   {
      TimeSpan good = DoGood();
      TimeSpan bad = DoBad();
      decimal ugly = DoUgly();

      Console.WriteLine("The good: " + good);
      Console.WriteLine("The bad:  " + bad);
      Console.WriteLine("The ugly: " + ugly);
   }

   static TimeSpan DoGood()
   {
      Stopwatch stopwatch = new Stopwatch();
      stopwatch.Start();

      Do();

      stopwatch.Stop();
      return stopwatch.Elapsed;
   }

   static TimeSpan DoBad()
   {
      DateTime begin = DateTime.Now;

      Do();

      DateTime end = DateTime.Now;
      return end - begin;
   }

   static decimal DoUgly()
   {
      long begin;
      QueryPerformanceCounter(out begin);

      Do();

      long end;
      QueryPerformanceCounter(out end);

      return GetSecondsElapsed(begin, end);
   }

   static void Do()
   {
      List<int> lst = new List<int>();

      for (int i = 0; i < 1000000; i++)
         lst.Add((i % 2 == 0 ? 1 : -1));

      int sum = 0;
      foreach (int i in lst)
         sum += i;
   }

   static decimal GetSecondsElapsed(long start, long stop)
   {
      long queryFrequency;
      QueryPerformanceFrequency(out queryFrequency);
      decimal result = Convert.ToDecimal(stop - start) / Convert.ToDecimal(queryFrequency);
      return Math.Round(result, 6);
   }
}

The output is the following:

>gen
The good: 00:00:00.0405179
The bad:  00:00:00.0500720
The ugly: 0.042730

The key takeaway of all this stuff is that DateTime.Now hasn't the best timer resolution to perform exact performance measurement. Rather you should use the Stopwatch class (introduced in .NET 2.0) which encapsulated the low-level platform stuff to perform an exact measurement. So the pattern looks like this:

Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();

// code to be tested

stopwatch.Stop();
return stopwatch.Elapsed; //alternative properties include ElapsedMilliseconds and ElapsedTicks

A last alternative is the following (cute) piece of code:

class PerformanceTester : IDisposable
{
   private Stopwatch stopwatch;
   private TextWriter output;

   public PerformanceTester(TextWriter output)
   {
      this.output = output;
      stopwatch = new Stopwatch();
      stopwatch.Start();
   }

   public void Dispose()
   {
      stopwatch.Stop();
      output.WriteLine(stopwatch.Elapsed);
   }
}

With this, you can do the following:

static void DoCute()
{
   using (new PerformanceTester(Console.Out))
   {
      Do();
   }
}

Isn't this cute :$

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# Nice initative to find code snippets - CodeKeep

Tuesday, April 11, 2006 11:32 PM by B# .NET Blog

Today I ran across CodeKeep, an online repository with tons of code snippets. Actually I was reviewing...

# re: Performance measurement in .NET 2.0 - the birth of Stopwatch

Wednesday, April 12, 2006 9:31 PM by Jaimi

you should decorate your DllImport with SuppressUnmanagedCodeSecurity attribute, so you have as little overhead as possible.