Click here to Skip to main content
6,594,932 members and growing! (14,973 online)
Email Password   helpLost your password?
Languages » C / C++ Language » General     Intermediate

Easy to use Performance Testing Component

By Jon Nethercott

A component to enable performance testing and timing of code in .NET.
C#, .NET CF, Mobile, .NET 1.0, .NET 1.1, .NET 2.0, Win2K, WinXP, Win2003VS.NET2003, VS2005, Dev
Posted:19 Jul 2004
Views:55,944
Bookmarked:31 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
26 votes for this article.
Popularity: 5.88 Rating: 4.16 out of 5
2 votes, 7.7%
1

2
2 votes, 7.7%
3
5 votes, 19.2%
4
17 votes, 65.4%
5

Visual Studio Screenshot

Overview

This article describes a timer component that allows timing to be done to an accuracy of microseconds.

Introduction

One of the tasks in software development that is often overlooked is performance testing. Most of the time, code is designed to run fast and then tested to make sure that it really is. Whilst there are many ways to test performance and identify slow bits of code, it is quite often difficult to precisely locate or time, specific sections of the code. Using normal time functions, including timer tick functions is only accurate to milliseconds at best. Also, you don't want to have to copy library classes or lots of code around in order to simply time a piece of code. Because of this, I decided to write a general purpose timer that would:

  1. Be able to time code to a resolution of a few microseconds.
  2. Be very easy to use.

Background

In order to have a timer that has a resolution in the order of microseconds, there are 2 possibilities that I am aware of:

  1. The RDTSC instruction: This is available on all Pentium and Athlon processors.
  2. QueryPerformanceCounter: This is a Windows API call to a performance counter which is generally running at speeds well in excess of 1MHz.

Although the first option is the most accurate and fastest, I decided to use the second option because that is much more portable. QueryPerformanceCounter is supported on all Windows platforms, including Pocket PC devices.

The second requirement was for the timer to be very easy to use. It was fairly obvious to me that the timer should be a strongly named class so that it could be put in the GAC. This would make it very easy to include in an assembly. I also felt that it would be even easier to use if it was a component. That way, if timing was being done on a form (Windows or Web), the component could be dragged from the toolbox onto the page.

Public Methods in the StopWatch Component

The component is very simple to use and has 2 main methods:

  1. Reset(): This resets the count to 0 and can be called anytime.
  2. Trace(): This has 2 overloads. One takes no parameters, and just displays the elapsed time in the debug output window. The other takes a string as a parameter, and displays the string before displaying the time. The time is displayed as either microseconds (us), milliseconds (ms), or seconds (s), depending on the elapsed time.

Implementation

The code relies on the two API calls:

  • QueryPerformanceFrequency
  • QueryPerformanceCounter

These are defined in KERNEL.DLL on Windows platforms, and CoreDll.dll on the Pocket PC, and are called using P/Invoke as follows:

#if NET_CF
    [System.Runtime.InteropServices.DllImport("CoreDll.dll")]
#else
    [System.Runtime.InteropServices.DllImport("Kernel32.dll")]
#endif
private static extern int QueryPerformanceFrequency(ref Int64 lpFrequency);

#if NET_CF
    [System.Runtime.InteropServices.DllImport("CoreDll.dll")]
#else
    [System.Runtime.InteropServices.DllImport("Kernel32.dll")]
#endif
private static extern int QueryPerformanceCounter(ref Int64 lpPerformanceCount);

NET_CF should be defined in the build settings if the library is going to be used on the Pocket PC.

The component contains a property called Time_us which is read-only and returns the elapsed time as follows:

public double Timer_us
{
    get
    {
        QueryPerformanceCounter(ref m_LastCount);
        Int64 Count = m_LastCount;
        Count -= m_TimerStartCount;
        return (double)Count / (double)m_TimerFreq * 1000000.0;
    }
}

The Reset and Trace methods are implemented as follows:

public void Reset()
{
    QueryPerformanceFrequency(ref m_TimerFreq);
    QueryPerformanceCounter(ref m_TimerStartCount);
}

public void Trace(string msg)
{
    double t1 = Timer_us;
    Int64 c1 = m_LastCount;
    StringBuilder s1 = new StringBuilder();
    if (t1 < 1000)
        s1.AppendFormat("{0} Time = {1} us", msg, t1.ToString("F2"));
    else if (t1 < 1000000)
        s1.AppendFormat("{0} Time = {1} ms", msg, (t1/1000).ToString("F2"));
    else
        s1.AppendFormat("{0} Time = {1} s", msg, (t1/1000000).ToString("F2"));
    System.Diagnostics.Trace.WriteLine(s1);
    //...trace compensation needed here!

}

The Trace.WriteLine statement takes a considerable time to execute (milliseconds). As the code is at the moment, this would seriously affect the displayed times. In order to try and compensate for this, I decided to get the time after the Trace statement has executed and subtract this from the start time. This means that if you call Trace continuously, you will get times that are different by about 1 or 2 us instead of times that are different by milliseconds. Although this means the displayed time is not a true indication of the elapsed time, I felt that this behavior was more useful. Unfortunately, I could not reliably compensate for the QueryPerformanceCounter call, so continuous calls to Trace resulted in increasing times of about 1.4us on my PC. Most of this time is because of the P/Invoke overhead. The code for this is shown below:

    double t2 = Timer_us;
    Int64 c2 = m_LastCount;
    m_TimerStartCount += (c2-c1); //Take account of the trace statement

The final part of the implementation is the deployment of the component. I wrote a batch file (as part of the build process) to copy the assembly to the Visual Studio directory and install it in the global assembly cache. This way, it can easily be added to the References and Component Toolbox. (In the Add References dialog, look for "Nethercott.Timing". And in the Add/Remove Items dialog in the Toolbox, look for "StopWatch".) Obviously, the component only has to be added once to the toolbox (e.g., under the Components tab) in order to be used on multiple solutions.

Using the Component

The component is very easy to use. There are two ways that it can be included. The easiest way is to drag the component from the toolbox onto a Windows or Web Form. The Reset() and Trace() methods can then be called as required in the code behind page. The other way to use the component is to include the component manually. This means adding a reference to the class, constructing the StopWatch object, and then calling the Reset() and Trace() methods as required. Although not absolutely necessary, it's probably a good idea to call Dispose() when the object is no longer required.

References

There are several other CodeProject articles on timing, and timer classes. Here are some of them:

History

  • 19-Jul-2004 - Initial version.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Jon Nethercott


Member
Jon studied at Kingston Polytechnic in the early 80s for a BSc(Hons) in Electronic Systems Engineering. His first job was at Marconi in Frimley - initially as a hardware engineer, and later in the applications group, writing defence apps in Coral66. He then worked on PDA apps at DIP Research in Guildford, and is currently a principal software engineer at Airspan communications in Uxbridge. Most of Jon's experience is using Microsoft products, C++, and more recently C# and the .NET technologies.
Occupation: Web Developer
Company: CodeWrite Ltd.
Location: United Kingdom United Kingdom

Other popular C / C++ Language articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 15 of 15 (Total in Forum: 15) (Refresh)FirstPrevNext
QuestionEvaluating Performance Pinmemberlowmodelrs5:08 24 Jan '07  
AnswerRe: Evaluating Performance PinmemberJon Nethercott2:01 25 Jan '07  
GeneralRe: Evaluating Performance Pinmemberlowmodelrs7:54 25 Jan '07  
GeneralA few suggestions... PinmemberStealthyMark2:56 12 Aug '04  
GeneralRe: A few suggestions... PinmemberJon Nethercott9:05 17 Aug '04  
GeneralFollow the aricle guidelines Pinmembernorm.net7:13 21 Jul '04  
GeneralRe: Follow the aricle guidelines PinmemberTom Archer11:58 21 Jul '04  
GeneralRe: Follow the aricle guidelines PinmemberAllen Anderson14:56 21 Jul '04  
GeneralRe: Follow the aricle guidelines Pinmembernorm.net21:59 21 Jul '04  
GeneralRe: Follow the aricle guidelines PinmemberAllen Anderson6:27 26 Jul '04  
GeneralRe: Follow the aricle guidelines Pinmembernorm.net21:58 21 Jul '04  
GeneralRe: Follow the aricle guidelines PinmemberTom Archer2:21 22 Jul '04  
GeneralRe: Follow the aricle guidelines Pinmembernorm.net3:22 22 Jul '04  
GeneralRe: Follow the aricle guidelines Pinmembercomputerguru923828:08 17 Dec '05  
GeneralRe: Follow the aricle guidelines Pinmembernorm.net21:35 19 Dec '05  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 19 Jul 2004
Editor: Smitha Vijayan
Copyright 2004 by Jon Nethercott
Everything else Copyright © CodeProject, 1999-2009
Web16 | Advertise on the Code Project