Skip to main content

C# class useful for timing the duration of an algorithm.

/******************************************************************************
Module:  OperationTimer.cs
Notices: Copyright (c) 2006-2010 by Jeffrey Richter and Wintellect
******************************************************************************/

using System;
using System.Threading;
using System.Diagnostics;

/// <summary>
/// This class is useful for timing the duration of an algorithm
/// </summary>
public sealed class OperationTimer : IDisposable
{
    private static Int32 s_NumOperationTimersStarted;
    private Stopwatch m_sw;
    private String m_text;
    private Int32 m_collectionCount;

    /// <summary>
    /// Constructs an OperationTimer with an empty text string
    /// </summary>
    public OperationTimer() : this(String.Empty) { }

    /// <summary>
    /// Constructs an OperationTimer with text identifying the operation
    /// </summary>
    /// <param name="text">Text describing the operation.</param>
    public OperationTimer(String text)
    {
        if (Interlocked.Increment(ref s_NumOperationTimersStarted) == 1)
        {
            PrepareForOperation();
        }

        m_text = text;
        m_collectionCount = GC.CollectionCount(0);
        m_sw = Stopwatch.StartNew();    // This should be the last statement in this method
    }

    /// <summary>
    /// Call this when the operation is done to see how long it took and how many GCs occurred.
    /// </summary>
    public void Dispose()
    {
        //
        // Console.WriteLine("{0,7:N0} (GCs={1,3}) {2}",
        //                   m_sw.Elapsed.TotalMilliseconds,
        //                   GC.CollectionCount(0) - m_collectionCount, m_text);
        // >> 2,611 (GCs=  3) List<String>
        // >> 3,001 (GCs=  1) ArrayList of String

        Console.WriteLine("  {0} (GCs={1:0##}) {2}",
                          (m_sw.Elapsed),
                          GC.CollectionCount(0) - m_collectionCount, m_text);
        // >> 00:00:02.4570664 (GCs=  3) List<String>
        // >> 00:00:02.9909267 (GCs=  1) ArrayList of String

        Interlocked.Decrement(ref s_NumOperationTimersStarted);
        m_sw = null;
    }

    private static void PrepareForOperation()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }
}

//////////////////////////////// End of File //////////////////////////////////

// Example Usage:

public static class Performance
{
    public static void ValueTypePerfTest()
    {
        const Int32 count = 100000000;

        using(new OperationTimer("List<Int32>"))
        {
            List<Int32> l = new List<Int32>();
            for (Int32 n = 0; n < count; n++)
            {
                l.Add(n);         // No boxing
                Int32 x = l[n];  // No unboxing
            }
            l = null;  // Make sure this gets GC'd
        }

        using(new OperationTimer("ArrayList of Int32"))
        {
            ArrayList a = new ArrayList();
            for (Int32 n = 0; n < count; n++)
            {
                a.Add(n);                // Boxing
                Int32 x = (Int32)a[n];  // Unboxing
            }
            a = null;  // Make sure this gets GC'd
        }
    }

    public static void ReferenceTypePerfTest()
    {
        const Int32 count = 100000000;

        using(new OperationTimer("List<String>"))
        {
            List<String> l = new List<String>();
            for (Int32 n = 0; n < count; n++)
            {
                l.Add("X");           // Reference copy
                String x = l[n];      // Reference copy
            }
            l = null;  // Make sure this gets GC'd
        }

        using(new OperationTimer("ArrayList of String"))
        {
            ArrayList a = new ArrayList();
            for (Int32 n = 0; n < count; n++)
            {
                a.Add("X");                 // Reference copy
                String x = (String)a[n];  // Cast check & reference copy
            }
            a = null;  // Make sure this gets GC'd
        }
    }
}

//
// Run performance in console...
internal class Program
{
    private static void Main(string[] args)
    {
        Console.WriteLine("ReferenceTypePerfTest...\n");
        Performance.ReferenceTypePerfTest();
        Console.WriteLine("\nDone.\n");

        Console.WriteLine("ValueTypePerfTest...\n");
        Performance.ValueTypePerfTest();
        Console.WriteLine("\nDone.\n");
    }
}

// -------------------------------------------------------------------
// Console output
// -------------------------------------------------------------------

// >> ReferenceTypePerfTest...
// >>
// >>   00:00:02.4570664 (GCs=003) List<String>
// >>   00:00:02.9909267 (GCs=001) ArrayList of String
// >>
// >> Done.
// >>
// >> ValueTypePerfTest...
// >>
// >>   00:00:00.8276096 (GCs=003) List<Int32>
// >>   00:00:06.9304192 (GCs=568) ArrayList of Int32
// >>
// >> Done.