- Table of Contents
- .NET Book Recommendations
- What Is .NET?
- The Microsoft .NET Framework
- The Common Language Runtime (CLR), the Common Type System (CTS), and the Common Language Specification (CLS)
- .NET Framework Class Library
- Visual Studio .NET
- .NET Enterprise Servers and .NET My Services
- .NET Compliant Languages
- C#
- Visual Basic .NET (VB .NET)
- ASP.NET
- XML Web Services
- ADO.NET
- XML.NET
- Windows Forms
- Why .NET?
- Displaying Errors with the Error Provider
- COM Interoperability
- Comparing Java and .NET
- Calling Unmanaged Code
- .NET Application Security
- Code Access Security
- .NET Standards Support
- Numeric Types in the .NET Framework
- Working with Strings
- Formatting Strings
- Trimming Character Strings
- Comparing Strings in .NET 2.0
- Arrays and Collections
- Arrays as Class Members
- Sorting a Multi-Dimensional Array
- File I/O (System.IO)
- Working with File Names
- Using the File System
- Working with Files and Directories
- Monitoring the File System
- Working with Streams
- Working with Text Encodings
- Working with Date and Time
- Extending the DateTime Class
- Fun with Dates
- Exceptions
- Delegates
- Events
- Asynchronous Programming
- Asynchronous File I/O
- Timers
- Random Numbers
- Serialization
- MultiThreading (System.Threading)
- Multi-Threading Overview
- The Managed Thread Pool
- Managed Threading
- Thread Synchronization
- Synchronizing Data Access
- Trace Debugging
- Tracing in .NET 2.0
- ASP.NET Trace
- Validating User Input in ASP.NET Web Pages
- Event Logging
- Monitoring Application Performance
- High Resolution Timing
- Introduction to .NET Performance Counters
- Performance Counters Concepts
- Viewing Performance Counters
- Types of Performance Counters
- Performance Counter Interface
- Accessing Performance Counters from Code
- Custom Performance Counters
- Creating Custom Performance Counters
- Deleting Performance Counter Categories
- Updating Performance Counters
- Performance Counter Type Reference
- Average Counters
- Difference Counters
- Rate Counters
- Sample Counters
- Accessing the Registry
- Accessing Environment Information
- Environment Variables in .NET 2.0
- Managing Windows Forms Applications
- Working with Email
- Working with Graphics
- Animating a Background
- Working with Images
- Drawing Cycloid Curves
- Simulating the Spirograph
- Building International Web Applications
- .NET Compact Framework
- Mobile Web Development with ASP.NET
- Speech Technologies
- Microsoft MapPoint Web Service
- Working with Typed DataSets
- Using Relationships in DataSets
- DataColumn Expressions
- Playing Simple Sounds
- Playing Sounds with .NET 2.0
- Returning an Image in a Web Page
- RSS
- Best Practices Project Structure
- Best Practices Application Blocks
- The Data Access Application Block
- The Exception Management Application Block
- Best Practices — Performance
- Best Practices — Performance and Scalability
- Best Practices - Testing
- Reading the Tea Leaves, 2005
- Predictions: A Look Back at 2005, and a Look Ahead to 2006
- .NET Downloads
- Application Deployment Overview
- Application Deployment — Versioning
- Application Deployment — Version Policy
- Application Deployment — Packaging and Distribution
- .NET Remoting Overview
- A Remoting Demonstration
- Remoting Configuration
- Remoting: Lifetimes and Leases
- Remoting: Other Issues
- Attributes
- Writing Custom Attributes
- Accessing Attributes in Code
- Reflection
- Class Design: Inheritance, Interface, or Composition?
- The TriTryst Game
- Console Applications in .NET 2.0
- New File I/O Methods in .NET 2.0
- Building Projects with MSBuild
- Unmanaged Callbacks in .NET 2.0
- Timer Troubles
- Non-Rectangular Windows Forms
- Windows Forms Transparency
- 10 Things I Hate About Visual Basic
- 10 Things I Hate About C#
- Background Processing with Idle Time
- Scaling Windows Forms
- Reading and Writing Binary Data
- New Memory Management Functions in .NET 2.0
- Compatibility Between .NET 1.1 and .NET 2.0
- Managed Debugging Assistants in .NET 2.0
- XDir: A Program for Viewing Directory Sizes
- The Microsoft.VisualBasic Namespace
- Operator Overloading
- Working with GPS Data
- Hidden Visual Studio Tools
- .NET 3.0
- The .NET 2.0 Stopwatch Class
- Nullable Types
- Drawing Rotated Text
- Unsafe Code
- Other .NET Languages
- Compiler Directives
- Safe Handles
- Predictions, 2007 Edition
- New Features in C# 3.0
- Generics
- Network Client Programming
- On the Misuse of Exceptions
- Maximum Object Size in .NET
- More on Maximum Object Sizes
- Keyed Collection Memory Limitations
- Matching String Endings
- Allocating Small Data Structures
- Grumbling About Limitations
- Some Thoughts on the Nature of What We Do
- Working with Predicates in Collections
- Working with DataReaders
- Outputting XML with XmlWriter
- Writing XML Data
- Working with Compression
- Another Look at Compressed Streams
- Compressing a Very Large File
- Canonical URIs
- Constructing URIs
- Using OneWayAttribute for Remote Calls
- Selecting a Garbage Collector
- Linked List
- Linked List Application - The MRU List
- Auto-implemented Properties in C#
- The HashSet Collection
- Looking Ahead: 2018
- An Experiment in Optimization
- A Larger Integer
- Extension Methods
- Language Integrated Query (LINQ)
- Variable Length Parameter Lists
- The ReaderWriterLockSlim Synchronization Primitive
- Sorting a Text File
- Sorting a Large Text File
- Using ListView with Large Data Sets
- LINQ One-Liners
- Regular Expression Optimization
- Random File I/O
- Computing the Size of a Structure
- More on Computing Structure Sizes
- UnmanagedMemoryStream
- Dynamically Loading Code
- Building a String Table
- Delegates Versus Function Pointers
- Visual Studio Editor Features
- A Simple Profile Timer
- New Features in C# 4.0
- IEnumerator or IList?
- New Features in .NET 4.0
- Set Operations with IEnumerable and HashSet
- Using File Locks
- Extending Object Functionality
- Clearing a HashSet
- When Hash Codes Matter
- Parsing Command Line Options
- Creating a Single-Instance Program
- Asynchronous Windows Forms Events
- The BackgroundWorker Component
- Fixing a Dumb Mistake
- Thinking About Multi-Threaded Programs
- JavaScript Object Notation
- Useful .NET-related Sites
- Markov Models
- Building an Order 0 Markov Model
- Higher Order Markov Models
- Webmaster's Guide to robots.txt
- An Overview of the Parallel Extensions to .NET
- Parallel Extensions Synchronization Objects
- Thread Safe Collections
- A Bug and a Conundrum
- Another Bug and an Answer
- Task Parallel Library
- Good and Bad Ideas in C#
- Parallel LINQ
- Copying Large Files
- Replacing File.Copy
- Learning from Our Mistakes
- Symbolic Links
- There Is No Easy Fix
- Tracking Hurricanes
- Examining Hurricane Data
- Searching for Multiple Strings
- Simple JSON Processing
- Aho-Corasick String Searching New
- Writing a Web Crawler New
- Web Crawler Politeness New
- Source Control Management New
- Informit Reference Library
High Resolution Timing
Last updated May 13, 2004.
If you need better than 10 millisecond resolution, you have to look outside of the .NET Framework classes. Windows provides a high-resolution timer that can give sub-millisecond resolution, but to get to it you have to call a Windows API function through runtime interop services.
The Windows high-performance timer interface consists of two functions: QueryPerformanceCounter and QueryPerformanceFrequency. QueryPerformanceCounter returns the current performance counter value in a 64-bit integer. You can use this function in the same way as you use the other timers: get the starting value, perform an operation, and then subtract the starting value from the ending value. That gives you the difference in performance counter values, but it doesn't provide a time reference. The time reference is supplied by QueryPerformanceFrequency. This function returns the performance frequencythe number of performance counter values per second. So the number of elapsed seconds is:
(ending_value starting_value) / frequency
For example, on my system QueryPerformanceFrequency returns 3579545. So each "tick" is 1/3579545 second, or about 0.28 microseconds. Sub-microsecond timing, what a deal! Well, almost.
Whereas it's true that QueryPerformanceCounter will give you sub-millisecond resolution, your accuracy is limited by the amount of time it takes to obtain the counter value. For example, on my 750 MHz Pentium it takes almost four microseconds per call to QueryPerformanceCounter. Since it takes two calls (one for the start value and one for the stop value), the overhead works out to eight microseconds. Still, that's 100 times more accurate than Environment.TickCount, and most likely good enough for most things that you'll be working with.
The C prototypes for the two high-resolution timer functions are:
BOOL QueryPerformanceFrequency(LARGE_INTEGER *lpFrequency); BOOL QueryPerformanceCounter(LARGE_INTEGER *lpPerformanceCount);
Both functions return a non-zero value if a high-resolution performance counter. If no such counter is installed, the functions return zero. The single parameter is a pointer to a 64-bit value in which the function returns the counter frequency or counter value. The equivalent C# and Visual Basic function definitions are:
[C#]
public static extern bool QueryPerformanceCounter(out long perfcount); public static extern bool QueryPerformanceFrequency(out long freq);
[Visual Basic]
Public Shared Function QueryPerformanceCounter(ByRef perfcount As Int64) As Boolean Public Shared Function QueryPerformanceFrequency(ByRef freq As Int64) As Boolean
In order to use these functions from a .NET program, you need to create a class that exposes the Windows API calls through runtime interop services. I created a class called PerfCount, shown here:
[C#]
public class PerfCount
{
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public static extern bool QueryPerformanceCounter(out long perfcount);
[System.Runtime.InteropServices.DllImport("Kernel32.dll")]
public static extern bool QueryPerformanceFrequency(out long freq);
public static long QueryPerformanceCounter()
{
long perfcount;
QueryPerformanceCounter(out perfcount);
return perfcount;
}
public static long QueryPerformanceFrequency()
{
long freq;
QueryPerformanceFrequency(out freq);
return freq;
}
}
[Visual Basic]
Public Class PerfCount
<System.Runtime.InteropServices.DllImport("Kernel32.dll")> _
Public Shared Function QueryPerformanceCounter(ByRef perfcount As Int64) As Boolean
End Function
<System.Runtime.InteropServices.DllImport("Kernel32.dll")> _
Public Shared Function QueryPerformanceFrequency(ByRef freq As Int64) As Boolean
End Function
Public Shared Function QueryPerformanceCounter() As Int64
Dim perfcount As Int64
QueryPerformanceCounter(perfcount)
Return perfcount
End Function
Public Shared Function QueryPerformanceFrequency() As Int64
Dim freq As Int64
QueryPerformanceFrequency(freq)
Return freq
End Function
End Class
You'll notice that the PerfCount provides overloaded versions of the functions that return the value directly rather than requiring you to pass an int64 parameter. It just seems more natural to write:
long freq = PerfCount.QueryPerformanceFrequency();
rather than the Windows API-ish:
Dim freq As Int64 PerfCount.QueryPerformanceFrequency(freq)
Once you've included this class in your project, you can use it to time operations in much the same way that the DateTime.Now and Environment.TickCount timers are used. The only difference is that you need to divide the elapsed tick count by the performance frequency in order to obtain the time in seconds. For example:
[C#]
long startCount = PerfCount.QueryPerformanceCounter();
DoSomething();
long stopCount = PerfCount.QueryPerformanceCounter();
long elapsedCount = stopCount - startCount;
double elapsedSeconds = (double)elapsedCount/PerfCount.QueryPerformanceFrequency();
Console.WriteLine("{0} seconds", elapsedSeconds);
[Visual Basic]
Dim startCount As Int64 = PerfCount.QueryPerformanceCounter()
DoSomething()
Dim stopCount As Int64 = PerfCount.QueryPerformanceCounter()
Dim elapsedCount = stopCount - startCount
Dim elapsedSeconds As Double = elapsedCount / PerfCount.QueryPerformanceFrequency()
Console.WriteLine("{0} seconds", elapsedSeconds)
You could extend the PerfCount class to include methods that convert an elapsed counter value to seconds, milliseconds, microseconds, or whatever time scale that you want.
Half A Solution
As you can see, there are many ways to get an elapsed time value for a particular piece of code. Which one you use depends mostly on how accurate you want to get. Regardless of the way you obtain elapsed time values, you've only solved half of the problem. The other half of the problem is reporting those values so that you can use them to analyze your application's performance. In the examples here, I've just been writing the values to the console. You could write them to the application's event log or add them to Trace output if you wanted. However you do it, you'll then need to create tools that extract the timing information from the logs. However you do it, it's a big job.
Remember also that elapsed processing time doesn't tell you much about your bottlenecks, even if you could time every line of code in the program. Even if you could identify the exact line of code that's causing the performance problem, that doesn't tell you what the problem is. Is the program CPU bound? Is disk I/O taking too long? Is there some weird network latency that's causing your program to hang? Those are but a few of the things that could be causing performance problems.
Elapsed time values only tell you how long something takes. That's very useful when you're trying to optimize a CPU intensive task. But when you add network traffic, database access, and the many other functions that applications perform, the only use of an elapsed time value is comparison: is this version faster than the previous one? If you want to do detailed performance analysis, you need to step into the world of performance counters.



Account Sign In
View your cart