Using a Stopwatch Timer to Measure Code Execution Time


If you are working on a production application that will ship on the .NET Compact Framework platform, chances are you will be doing performance analysis on the application code. As this chapter is being written, the performance profiling tools on the market for the .NET Compact Framework are few and far between. Despite this lack of performance tools, there are some simple techniques that can be used to profile an application's performance traits.

First, it is important to have a reliable way to measure the execution time of a block of code. The .NET Compact Framework provides the TickCount property on the Environment class in the System namespace. This property can be used to calculate the execution time of a block of code. For example, the code in Listing 15.1 measures the execution time of the XmlDocument.Load method.

Listing 15.1
 C# XmlDocument dom = new XmlDocument(); int startTime = System.Environment.TickCount; dom.Load("test.xml"); int executionTime = System.Environment.TickCount - startTime; VB Dim startTime As Int32 Dim executionTime As Int32 Dim dom As New XmlDocument startTime = System.Environment.TickCount dom.Load("test.xml") executionTime = System.Environment.TickCount - startTime 

The executionTime variable contains the number of milliseconds the XmlDocument.Load method call took to execute. Although this is a very simple way to measure execution time, the TickCount property can return inaccurate results. The TickCount property is based on the OS implementation of the GetTickCount function. The GetTickCount function retrieves the number of milliseconds that have elapsed since the system was started. The resolution of the return value is limited to the system timer, which does not usually have a high resolution. Also, the GetTickCount counter will wrap around to zero if the system is running continuously for 49.7 days.

A high-resolution timer, like QueryPerformanceCounter , will give more accurate and reliable results. If a high-resolution performance counter does exist on the system, the QueryPerformanceCounter method can be used to retrieve the frequency of the counter in counts per second. The frequency of the counter is processor dependent. On some processors the frequency might be the cycle rate of the processor clock.

The QueryPerformanceCounter method retrieves the current value of the high-resolution performance counter. By calling this function at the beginning and end of a section of code, the counter is used as a high-resolution timer. Suppose that QueryPerformanceFrequency indicates that the frequency of the high-resolution performance counter is 50,000 counts per second. If the application calls QueryPerformanceCounter immediately before and immediately after the section of code to be timed, the counter values might be 3000 and 6500, respectively. These values would indicate that .07 seconds (3500 counts) elapsed while the code executed.

The .NET Compact Framework does not provide an API to access these functions. Despite this, we can use Platform Invocation, P/Invoke, to get access to this high-resolution counter. The QueryPerformanceCounter and QueryPerformanceFrequency functions are located in coredll.dll . The DllImport attributes can provide access to these functions. It is important to note that QueryPerformanceCounter and QueryPerformanceFrequency provide access only to a high-resolution timer. If the original equipment manufacturer, OEM, did not provide an implementation of a high-resolution counter, then these APIs will fail. If this is the case, your timer code should fall back to the Environment.TickCount property or some other timer implementation. Listing 15.2 contains an example of a stopwatch-like timer using a high-resolution counter.

Listing 15.2
 C# public sealed class StopWatch {   // Native Methods   [DllImport("coredll.dll")]   public static extern bool QueryPerformanceCounter(out long c);   [DllImport("coredll.dll")]   public static extern bool QueryPerformanceFrequency(out long c);   // Static data   static long frequency;   static bool perfCounterSupported;   // Instance Data   long startTime = 0;   // Initialize the counter frequency static   static StopWatch() {     perfCounterSupported = QueryPerformanceFrequency (out frequency);   }   // Start the StopWatch   public void Start() {     startTime = TickCount;   }   // Stop the StopWatch   public long Stop() {     if(perfCounterSupported)       return (long)((float)(TickCount-startTime)/frequency)*1000;     else       return (TickCount - startTime);   }   // Get the current tick count   public static long TickCount {     get {       long val;       bool perfCounterSupported = QueryPerformanceCounter(out val);       if (perfCounterSupported)         return val;       else         return (long)System.Environment.TickCount;     }   } } VB Public Class StopWatch     ' Native Methods     Declare Function QueryPerformanceCounter Lib _     "coredll.dll" (ByRef c As Long) As Boolean     Declare Function QueryPerformanceFrequency Lib _     "coredll.dll" (ByRef c As Long) As Boolean     ' Static Data     Shared frequency As Long     Shared perfCounterSupported As Boolean     ' Instance Data     Dim startTime As Long     ' Initialize the counter frequency     Shared Sub New()         perfCounterSupported = QueryPerformanceFrequency(frequency)     End Sub     ' Initialize the instance data     Sub New()         startTime = 0     End Sub     ' Start the StopWatch     Public Sub Start()         startTime = TickCount()     End Sub     ' Stop the StopWatch     Public Function [Stop]() As Long         If perfCounterSupported Then             Return CLng(CDbl(TickCount() - startTime) / frequency * 1000)         Else             Return (TickCount() - startTime)         End If     End Function     ' Get the current tick count     Public Shared ReadOnly Property TickCount() As Long         Get             Dim val As Long             perfCounterSupported = QueryPerformanceCounter(val)             If perfCounterSupported Then                 Return val             Else                 Return CLng(System.Environment.TickCount)             End If         End Get     End Property End Class 

This code provides a general-purpose, high-resolution stopwatch timer that can measure the execution time of an operation. The Stop method returns the number of milliseconds that have elapsed since calling the Start method. Listing 15.3 contains an example of how one could use the StopWatch class.

Listing 15.3
 C# StopWatch timer = new StopWatch(); timer.Start(); Thread.Sleep(1000); long elapseTime = timer.Stop(); MessageBox.Show("The stop watch recorded " + elapseTime + "ms"); VB Dim timer As New StopWatch Dim elapseTime As Int64 timer.Start() Thread.Sleep(1000) elapseTime = timer.Stop() MessageBox.Show("The stop watch recorded " & elapseTime & "ms") 


Microsoft.NET Compact Framework Kick Start
Microsoft .NET Compact Framework Kick Start
ISBN: 0672325705
EAN: 2147483647
Year: 2003
Pages: 206

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net