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; } } }
Stopwatch sw1 = Stopwatch.StartNew(); Thread.Sleep(20); sw1.Stop(); Debug.WriteLine("Slept for 5 milliseconds ... actually: {0}", sw1.ElapsedMilliseconds);
Greatp post. Exactly what is missing in silverlight.
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.
I’m not sure why you’re suggesting those changes Chad?
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.
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.
Thanks for the code. I could not make it work as is but Chad Riddle’s comments worked for me.
Sree
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.
There is a mistake. Has been said 3 times by now. this is the 4th. The fact you say the documentation leads you to think its correct, suggest you haven’t tested it nor read the documentation correctly.
http://msdn.microsoft.com/en-us/library/system.environment.tickcount.aspx
Clearly states it returns a value in milliseconds and not ticks.
Other then that, a big thanks for the code :) Works fine after some minor bug fixing
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; }
}
I’ve updated the code, and tested it. Thanks all for the suggestions. Not sure why my tests worked when I wrote it.
[…] recently made a fix to a Silverlight stopwatch class I wrote last […]
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
@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!