Silverlight Stopwatch class in C#

I needed a simple method for doing some timings in Silverlight. Silverlight lacks the high performance query counter that is available natively in Windows (and available in .NET 3.5 for example), but it does have a TickCount. My code (mostly) mirrors the existing .NET Stopwatch class using the TickCount property of the Environment class.

Just create an instance of the class and call Start to start the timer, and then get the value of one of the  ElapsedNNN properties to get a live reading without stopping the timer or the Stop function to stop the timer completely.

Update April 4, 2010:

Even though there were some doubters in the comments suggesting I didn’t test the original version that was here – I had … so I’m confused as to why it had worked for me, yet not in current versions of Silverlight. In any case, I’ve updated the code and have included a sample of how to use it. Thanks to those who provided suggested fixes.

using System;
using System.Diagnostics;

namespace WiredPrairie.Silverlight
{
    /// <summary>
    /// Stopwatch is used to measure the general performance of Silverlight functionality. Silverlight
    /// does not currently provide a high resolution timer as is available in many operating systems,
    /// so the resolution of this timer is limited to milliseconds. This class is best used to measure
    /// the relative performance of functions over many iterations.
    /// </summary>
    public sealed class Stopwatch
    {
        private long _startTick;
        private long _elapsed;
        private bool _isRunning;

        /// <summary>
        /// Creates a new instance of the class and starts the watch immediately.
        /// </summary>
        /// <returns>An instance of Stopwatch, running.</returns>
        public static Stopwatch StartNew()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            return sw;
        }

        /// <summary>
        /// Creates an instance of the Stopwatch class.
        /// </summary>
        public Stopwatch() { }

        /// <summary>
        /// Completely resets and deactivates the timer.
        /// </summary>
        public void Reset()
        {
            _elapsed = 0;
            _isRunning = false;
            _startTick = 0;
        }

        /// <summary>
        /// Begins the timer.
        /// </summary>
        public void Start()
        {
            if (!_isRunning)
            {
                _startTick = GetCurrentTicks();
                _isRunning = true;
            }
        }

        /// <summary>
        /// Stops the current timer.
        /// </summary>
        public void Stop()
        {
            if (_isRunning)
            {
                _elapsed += GetCurrentTicks() - _startTick;
                _isRunning = false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the instance is currently recording.
        /// </summary>
        public bool IsRunning
        {
            get { return _isRunning; }
        }

        /// <summary>
        /// Gets the Elapsed time as a Timespan.
        /// </summary>
        public TimeSpan Elapsed
        {
            get { return TimeSpan.FromMilliseconds(ElapsedMilliseconds); }
        }

        /// <summary>
        /// Gets the Elapsed time as the total number of milliseconds.
        /// </summary>
        public long ElapsedMilliseconds
        {
            get { return GetCurrentElapsedTicks() / TimeSpan.TicksPerMillisecond; }
        }

        /// <summary>
        /// Gets the Elapsed time as the total number of ticks (which is faked
        /// as Silverlight doesn't have a way to get at the actual "Ticks")
        /// </summary>
        public long ElapsedTicks
        {
            get { return GetCurrentElapsedTicks(); }
        }

        private long GetCurrentElapsedTicks()
        {
            return (long) (this._elapsed + (IsRunning ? (GetCurrentTicks() - _startTick) : 0));
        }

        private long GetCurrentTicks()
        {
            // TickCount: Gets the number of milliseconds elapsed since the system started.
            return Environment.TickCount * TimeSpan.TicksPerMillisecond;
        }
    }
}


Sample usage:

Stopwatch sw1 = Stopwatch.StartNew();
Thread.Sleep(20);
sw1.Stop();
Debug.WriteLine("Slept for 5 milliseconds ... actually: {0}", sw1.ElapsedMilliseconds);

13 Comments

  1. Environment.TickCount: Gets the number of milliseconds elapsed since the system started. Not actual ticks.

    Therefore, instead of new TimeSpan(GetCurrentElapsed()); it should be TimeSpan.FromMilliseconds(GetCurrentElapsed());

    And you need to remove the divide by TimeSpan.TicksPerMilliseconds

    With those changes this is a great class. Thanks for the code.

  2. There is an error in this implementation. Because TimeSpan.TicksPerMillisecond in mos of cases does not corespondent with ticks/s used for Environment.TickCount computing. You should to review your code.

  3. Popsa — if that’s the case, what would the right answer be? The documentation doesn’t lead me to believe it’s wrong, and that would be an inconsistency in the framework.

  4. Pospa and Chad Riddle are saying the samething.
    Environment.TickCount is the milliseconds.
    From MSDN:
    TickCount is different from the Ticks property, which is the number of 100-nanosecond intervals that have elapsed since 1/1/0001, 12:00am.

  5. Fixed functions:

    // Timespan expects the value indiciated as 100ns .. thats 1 / 10000 of a ms.
    public TimeSpan Ellapsed
    {
    get { return new TimeSpan( GetCurrentElapsed() * 10000 ); }
    }

    // GetCurrentElapsed() returns the MS already, no need to adjust that
    public long EllapsedMilliseconds
    {
    get { return GetCurrentElapsed(); }
    }

    // getting value in milliseconds, gotta recalculate to turn it into ticks
    public long EllapsedTicks
    {
    get { return GetCurrentElapsed() * TimeSpan.TicksPerMillisecond; }
    }

    1. I’ve updated the code, and tested it. Thanks all for the suggestions. Not sure why my tests worked when I wrote it.

  6. Hi,
    This class will be used in my solution by files shared between Silverlight and .NET 4.0.
    So for compatibility issue, you should correct the word Ellapsed with Elapsed. It’s just cosmetic, but it can produce compilation problem in case of shared code.
    Anyway Thanx
    -Claudio

    1. @claudio: Whoops! Must have done a search/replace and managed to spell it wrong. Need to spell check my APIs! :) I’ve fixed the problem. Thanks for the heads-up!

Comments are closed.