65.9K
CodeProject is changing. Read more.
Home

A simple way to use a Stopwatch

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.67/5 (2 votes)

Nov 28, 2011

CPOL
viewsIcon

26667

Making Stopwatches easier to insert (and remove) when looking for bottlenecks

My tool of choice for finding out how long a piece of code executes is the System.Diagnostics.Stopwatch. In many cases (mostly utility console applications), I can add one, use it, and report its Elapsed time very easily. But last week, I needed to investigate some convoluted code that I didn't write. I needed to time several different areas without affecting the structure of the classes and then remove my code when I was done. I also wanted a count of how many times the Stopwatch was started, so I wrapped a Stopwatch in a very simple class:
namespace PIEBALD.Types
{
  public class Stopwatch : System.IDisposable
  {
    private readonly System.Diagnostics.Stopwatch stopwatch ;
 
    public Stopwatch
    (
    )
    {
      this.CountOfStarts = 0 ;
      this.stopwatch = new System.Diagnostics.Stopwatch() ;
      return ;
    }
 
    public virtual ulong CountOfStarts { get ; private set ; }
 
    public virtual System.TimeSpan
    Elapsed
    {
      get
      {
        return ( this.stopwatch.Elapsed ) ;
      }
    } 
 
    public virtual void
    Start
    (
    )
    {
      if ( !this.IsRunning )
      {
        this.CountOfStarts++ ;
        this.stopwatch.Start() ;
      }
      return ;
    }
 
    public virtual void
    Stop
    (
    )
    {
      this.stopwatch.Stop() ;
      return ;
    }
 
    public virtual void
    Dispose
    (
    )
    {
      this.Stop() ;
      return ;
    }
 
    public override string
    ToString
    (
    )
    {
      return ( System.String.Format
      (
        "{0} elapsed during {1} period{2}"
      ,
        this.Elapsed
      ,
        this.CountOfStarts
      ,
        this.CountOfStarts==1?"":"s"
      ) ) ;
    }
  }
}
I pass through a few other methods as well, but they're less important. The important bit is the Start that increments the count, and the Dispose that stops the Stopwatch. Then I wrapped a Dictionary in a static class:
namespace PIEBALD.Types
{
  public static class StopwatchOmatic
  {
    public static System.Collections.Generic.Dictionary<string,PIEBALD.Types.Stopwatch> Items { get ; private set ; }
 
    static StopwatchOmatic
    (
    )
    {
      Items = new System.Collections.Generic.Dictionary<string,PIEBALD.Types.Stopwatch>() ;
      return ;
    }
 
    public static PIEBALD.Types.Stopwatch
 
    Stopwatch
    (
      string Name
    )
    {
      PIEBALD.Types.Stopwatch result ;
      if ( !Items.TryGetValue ( Name , out result ) )
      {
        Items [ Name ] = result = new PIEBALD.Types.Stopwatch() ;
      }
      return ( result ) ;
    }
 
    public static PIEBALD.Types.Stopwatch
    Start
    (
      string Name
    )
    {
      PIEBALD.Types.Stopwatch result = Stopwatch ( Name ) ;
      result.Start() ;
      return ( result ) ;
    }
 
    public static System.Collections.Generic.IEnumerable<string>
    Results
    {
      get
      {
        foreach
        (
System.Collections.Generic.KeyValuePair<string,PIEBALD.Types.Stopwatch> p
        in
          Items
        )
        {
          yield return ( System.String.Format
          (
            "Stopwatch {0} indicates {1}"
          ,
            p.Key
          ,
            p.Value
          ) ) ;
        }
        yield break ;
      }
    }
  }
}
In this way, it's quite simple to wrap some code I want to time like so:
using ( PIEBALD.Types.Stopwatch sw = PIEBALD.Types.StopwatchOmatic.Start ( "Loop 1" ) )
{
  /* Some suspicious code */
  /* You may also use sw.Stop() in here if your needs require it */
}
Then report the Elapsed times of the various Stopwatches like this:
foreach ( string s in PIEBALD.Types.StopwatchOmatic.Results )
{
  System.Console.WriteLine ( s ) ;
}
So adding and removing Stopwatches, ensuring that they Stop in case of an Exception, then reporting on them has become much simpler.