Introduction
Setting up and using Performance Counters with the .NET API requires a bit of setup and configuration. This article presents a generalized code block which can be used in a common scenario for simple performance benchmarking.
Background
Finding how fast your application runs would be tricky business lacking any way to measure it. In Windows, we've got the built-in Performance Counters which can easily time operations and gather statistics about your software while it executes.
.NET offers a fairly robust API for accessing Performance Counters. It exposes classes and methods for setting and incrementing the counter values, and also the ability to create all the different varieties directly from your program. For an excellent starting point for using all of these, check out An Introduction To Performance Counters by Michael Groeger and Using performance counters to gather performance data by Shivprasad koirala.
As some superhero may have mentioned, "With great flexibility comes great complexity", so we'll look at a way to wrap a common usage with a reusable code block.
Using the Code
My first annoyance with the performance counter classes in .NET is the need to initialize two different classes for a single counter - one for creating it and another for interacting with the values. Since they have several overlapping properties which, by the way, need to be exactly the same or if they won't work, the definitions can be easily coupled:
class CounterCreationCounter
{
public CounterCreationData Creation { get; private set; }
public PerformanceCounter Counter { get; private set; }
public CounterCreationCounter(
string counterName, string counterHelp,
PerformanceCounterType counterType, string categoryName)
{
Creation = new CounterCreationData {
CounterName = counterName,
CounterHelp = counterHelp,
CounterType = counterType };
Counter = new PerformanceCounter {
CategoryName = cate2goryName,
CounterName = counterName,
MachineName = MACHINE_NAME,
ReadOnly = false };
}
}
Next, I want a way to define a group of common counters that will be relevant for measuring operations while benchmarking. To keep the article a reasonable size, I've selected just two:
- Number of operations per second
- Average duration of the operation in ticks
There are several more (Total number of operations, and Most recent duration) in the source code which are defined in the same manner.
class OperationalCounterGroup
{
private readonly CounterCreationCounter _countPerSec;
private readonly CounterCreationCounter _averageDuration;
private readonly CounterCreationCounter _averageDurationBase;
public OperationalCounterGroup(string operationName, string categoryName)
{
_countPerSec
= new CounterCreationCounter(
string.Format("{0} # / sec", operationName),
string.Format("Number of {0} per second", operationName),
PerformanceCounterType.RateOfCountsPerSecond32,
categoryName);
_averageDuration
= new CounterCreationCounter(
string.Format("{0} avg duration", operationName),
string.Format("Average duration for {0} in ticks", operationName),
PerformanceCounterType.AverageTimer32,
categoryName);
_averageDurationBase
= new CounterCreationCounter(
string.Format("{0} avg duration base", operationName),
string.Format("Average duration for {0} in ticks base", operationName),
PerformanceCounterType.AverageBase,
categoryName);
}
public void OperationCompleted(TimeSpan elapsed)
{
_countPerSec.Counter.Increment();
_averageDuration.Counter.IncrementBy(elapsed.Ticks);
_averageDurationBase.Counter.Increment();
}
public IEnumerable<countercreationdata /> GetCreationData()
{
yield return _countPerSec.Creation;
yield return _averageDuration.Creation;
yield return _averageDurationBase.Creation;
}
}
Notice that the OperationCompleted
method has the duty of incrementing all of the counters once an operation has been completed. Now, how is all of this wired to something we'd want to actually measure? First, I'll define an interface which exposes an OpeartionCompleted
event that will be raised by the object when it has something to measure.
public delegate
void OperationCompletedHandler(string operationName, TimeSpan elapsed);
public interface IOperationCompletable
{
event OperationCompletedHandler OperationCompleted;
}
... and here is the OperationPerformanceCounter
class to glue it all together. It requires an object implementing IOperationCompletable
, along with a list of operations that will be measured.
class OperationPerformanceCounter
{
private readonly string _category;
private readonly string[] _handledOperations;
private readonly IDictionary<string, OperationalCounterGroup> _counterGroups;
private void OperationCompleted(string operationName, TimeSpan elapsed)
{
if (_counterGroups.ContainsKey(operationName)) {
_counterGroups[operationName].OperationCompleted(elapsed);
}
}
public OperationPerformanceCounter(string categoryName,
string[] handledOperations,
IOperationCompletable operationCompletable)
{
_category = categoryName;
_handledOperations = handledOperations;
_counterGroups =
(from c in _handledOperations
select new {Name = c, Group =
new OperationalCounterGroup(c, _category)})
.ToDictionary(o => o.Name, o => o.Group);
UpdateOrCreateCategory((from c in _counterGroups
from cd in c.Value.GetCreationData()
select cd).ToArray());
operationCompletable.OperationCompleted += OperationCompleted;
}
}
The UpdateOrCreateCategory
method handles all of the Performance Counter creation automatically. It will also recognize if there are new (or removed) operations and update the local counters:
void UpdateOrCreateCategory(CounterCreationData[] counterCreationData)
{
if (PerformanceCounterCategory.Exists(_category))
{
var installedCounters = new HashSet<string>(
from ec in new PerformanceCounterCategory(_category).GetCounters()
select ec.CounterName);
var knownCounters = new HashSet<string>(
from c in counterCreationData
select c.CounterName);
if (!installedCounters.SetEquals(knownCounters))
{
DeleteCategory();
CreateCategory(counterCreationData);
}
}
else
{
CreateCategory(counterCreationData);
}
}
void CreateCategory(CounterCreationData[] counterCreationData)
{
PerformanceCounterCategory.Create(
_category, "", PerformanceCounterCategoryType.Unknown,
new CounterCreationDataCollection(counterCreationData));
PerformanceCounter.CloseSharedResources();
}
The code to configure and use OperationPerformanceCounter
is quite simple. First, an object implementing IOperationCompletable
. Here is a demonstration - this object can raise events for two operations that we're interested in - FastOp
and SlowOp
.
class DemoObject : IOperationCompletable
{
public event OperationCompletedHandler OperationCompleted;
private int _fastOpSleep;
public void FastOp()
{
Stopwatch sw = new Stopwatch();
sw.Start();
Thread.Sleep(_fastOpSleep);
sw.Stop();
_fastOpSleep += 1;
OperationCompleted("FastOp", sw.Elapsed);
}
private int _slowOpSleep;
public void SlowOp()
{
Stopwatch sw = new Stopwatch();
sw.Start();
Thread.Sleep(_slowOpSleep);
sw.Stop();
_slowOpSleep += 2;
OperationCompleted("SlowOp", sw.Elapsed);
}
}
And a simple harness in a console application. It calls the operations in separate threads so that the Performance Monitor graphs will directly overlap.
static void Main()
{
var demoObject = new DemoObject();
var opCounter =
new OperationPerformanceCounter(
"CodeProject - Simplified Performance Counters",
new[] {"FastOp", "SlowOp"},
demoObject);
var threads
= new[]
{new Thread(() => LoopAction(demoObject.FastOp)),
new Thread(() => LoopAction(demoObject.SlowOp))};
foreach (var t in threads)
t.Start();
foreach (var t in threads)
t.Join();
}
private static void LoopAction(Action action)
{
for (var i = 0; i < 200; i++)
action();
}
Running this will add the performance counters and record the following data - here are the resulting graphs created while comparing "Operations per Second" and "Average Duration" for "FastOp
" and "SlowOp
".

History
- 20th March, 2010: Initial post