Refactoring - Performance Counters Solution revisited
Posted On August 29, 2008 by Sneha Philipose filed under
Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a 'refactoring') does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it's less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring.
In my previous article I had done an in depth exposure to Performance counters. In this article we will refactor that solution. We will slowly proceed with small changes; remove the flaws in the solution and finally evolve into a better designed solution.
Let us now visit our code. What do we have?
Note: I have provided the old application so that you can directly refer to the code.

- A PerformanceCounterSample class file that has all the code which has the following responsibility
- Creating performance counters
- Instantiate performance counters
- Increment the performance counters
- Does all the processinmg
Note: PerformanceCounterSampleStarter is the user of our class.

- Moreover the category name, performance counter names need to be hard coded every time we use the solution.
- There is a repetition of steps.
We will need to do some polishing so that our code looks professional.
Section 1: Refactoring the constructor
The PerformanceCounterSample constructor looks like this.
public PerformanceCounterSample()
{
if (!PerformanceCounterCategory.Exists("MyCategory"))
{
CounterCreationDataCollection counters = new CounterCreationDataCollection();
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CounterCreationData totalOps = new CounterCreationData();
totalOps.CounterName = "# operations executed";
totalOps.CounterHelp = "Total number of operations executed";
totalOps.CounterType = PerformanceCounterType.NumberOfItems32;
counters.Add(totalOps);
// 2. counter for counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
CounterCreationData opsPerSecond = new CounterCreationData();
opsPerSecond.CounterName = "# operations / sec";
opsPerSecond.CounterHelp = "Number of operations executed per second";
opsPerSecond.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
counters.Add(opsPerSecond);
// 3. counter for counting average time per operation: PerformanceCounterType.AverageTimer32
CounterCreationData avgDuration = new CounterCreationData();
avgDuration.CounterName = "average time per operation";
avgDuration.CounterHelp = "Average duration per operation execution";
avgDuration.CounterType = PerformanceCounterType.AverageTimer32;
counters.Add(avgDuration);
// 4. base counter for counting average time per operation: PerformanceCounterType.AverageBase
CounterCreationData avgDurationBase = new CounterCreationData();
avgDurationBase.CounterName = "average time per operation base";
avgDurationBase.CounterHelp = "Average duration per operation execution base";
avgDurationBase.CounterType = PerformanceCounterType.AverageBase;
counters.Add(avgDurationBase);
// create new category with the counters above
PerformanceCounterCategory.Create("MyCategory", "Sample category for Codeproject", counters);
}
// create counters to work with
_TotalOperations = new PerformanceCounter();
_TotalOperations.CategoryName = "MyCategory";
_TotalOperations.CounterName = "# operations executed";
_TotalOperations.MachineName = ".";
_TotalOperations.ReadOnly = false;
_TotalOperations.RawValue = 0;
_OperationsPerSecond = new PerformanceCounter();
_OperationsPerSecond.CategoryName = "MyCategory";
_OperationsPerSecond.CounterName = "# operations / sec";
_OperationsPerSecond.MachineName = ".";
_OperationsPerSecond.ReadOnly = false;
_OperationsPerSecond.RawValue = 0;
_AverageDuration = new PerformanceCounter();
_AverageDuration.CategoryName = "MyCategory";
_AverageDuration.CounterName = "average time per operation";
_AverageDuration.MachineName = ".";
_AverageDuration.ReadOnly = false;
_AverageDuration.RawValue = 0;
_AverageDurationBase = new PerformanceCounter();
_AverageDurationBase.CategoryName = "MyCategory";
_AverageDurationBase.CounterName = "average time per operation base";
_AverageDurationBase.MachineName = ".";
_AverageDurationBase.ReadOnly = false;
_AverageDurationBase.RawValue = 0;
}
What do we observe? The constructor is very long and there is a lot of repetition.
According to the best practices, method should be short enough, may be 10-15 lines of code.
The following code is repeated with just changes in the assignment of properties of CounterCreationData.
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CounterCreationData totalOps = new CounterCreationData();
totalOps.CounterName = "# operations executed";
totalOps.CounterHelp = "Total number of operations executed";
totalOps.CounterType = PerformanceCounterType.NumberOfItems32;
counters.Add(totalOps);
Also there is repetition of the following code.
_TotalOperations = new PerformanceCounter();
_TotalOperations.CategoryName = "MyCategory";
_TotalOperations.CounterName = "# operations
We will move the code that is common to a seperate function and thus avoid repetition. Hence we will refactor and add the following two helper functions.
public static void CreateCounter(CounterCreationDataCollection counters, string counterName, string counterHelp, PerformanceCounterType counterType)
{
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CounterCreationData performanceCounter = new CounterCreationData(counterName, counterHelp, counterType);
counters.Add(performanceCounter);
}
public static PerformanceCounter InstantiateCounter(string categoryName, string counterName)
{
PerformanceCounter performanceCounter;
performanceCounter = new PerformanceCounter(categoryName, counterName);
performanceCounter.MachineName = ".";
performanceCounter.ReadOnly = false;
performanceCounter.RawValue = 0;
return performanceCounter;
}
Hence our new constructor will look like this
public PerformanceCounterSample()
{
if (PerformanceCounterCategory.Exists("MyCategory"))
PerformanceCounterCategory.Delete("MyCategory");
if (!PerformanceCounterCategory.Exists("MyCategory"))
{
CounterCreationDataCollection counters = new CounterCreationDataCollection();
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CreateCounter(counters, "# operations executed", "Total number of operations executed", PerformanceCounterType.NumberOfItems32);
// 2. counter for counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
CreateCounter(counters, "# operations / sec", "Number of operations executed per second", PerformanceCounterType.RateOfCountsPerSecond32);
// 3. counter for counting average time per operation: PerformanceCounterType.AverageTimer32
CreateCounter(counters, "average time per operation", "Average duration per operation execution", PerformanceCounterType.AverageTimer32);
// 4. base counter for counting average time per operation: PerformanceCounterType.AverageBase
CreateCounter(counters, "average time per operation base", "Average duration per operation execution base", PerformanceCounterType.AverageBase);
// create new category with the counters above
PerformanceCounterCategory.Create("MyCategory", "Sample category for Codeproject", counters);
}
// create counters to work with
TotalOperations = InstantiateCounter("MyCategory", "# operations executed");
_OperationsPerSecond = InstantiateCounter("MyCategory", "# operations / sec");
_AverageDuration = InstantiateCounter("MyCategory", "average time per operation");
_AverageDurationBase = InstantiateCounter("MyCategory", "average time per operation base");
}
Section 2: Refactoring the constructor continued
Still our constructor is long. Moreover it does two tasks; one is creating the performance counter and the other is instantiating it. We will split the code and move the code to two new functions.
private static void Create()
{
if (PerformanceCounterCategory.Exists("MyCategory"))
PerformanceCounterCategory.Delete("MyCategory");
if (!PerformanceCounterCategory.Exists("MyCategory"))
{
CounterCreationDataCollection counters = new CounterCreationDataCollection();
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CreateCounter(counters, "# operations executed", "Total number of operations executed", PerformanceCounterType.NumberOfItems32);
// 2. counter for counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
CreateCounter(counters, "# operations / sec", "Number of operations executed per second", PerformanceCounterType.RateOfCountsPerSecond32);
// 3. counter for counting average time per operation: PerformanceCounterType.AverageTimer32
CreateCounter(counters, "average time per operation", "Average duration per operation execution", PerformanceCounterType.AverageTimer32);
// 4. base counter for counting average time per operation: PerformanceCounterType.AverageBase
CreateCounter(counters, "average time per operation base", "Average duration per operation execution base", PerformanceCounterType.AverageBase);
// create new category with the counters above
PerformanceCounterCategory.Create("MyCategory", "Sample category for Codeproject", counters);
}
}
private void Instantiate()
{
// create counters to work with
_TotalOperations = InstantiateCounter("MyCategory", "# operations executed");
_OperationsPerSecond = InstantiateCounter("MyCategory", "# operations / sec");
_AverageDuration = InstantiateCounter("MyCategory", "average time per operation");
_AverageDurationBase = InstantiateCounter("MyCategory", "average time per operation base");
}
Our new constructor will look like this.
public PerformanceCounterSample()
{
Create();
Instantiate();
}
Section 3: PerformanceCountersHelpers: A new helper class
We will now refactor the PerformanceCounterSample class. CreateCounter and InstantiateCounter are static independent functions. They server as helpers and can be safely moved to a separate class PerformanceCountersHelpers.
public class PerformanceCountersHelpers
{
public static void CreateCounter(CounterCreationDataCollection counters, string counterName, string counterHelp, PerformanceCounterType counterType)
{
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
CounterCreationData performanceCounter = new CounterCreationData(counterName, counterHelp, counterType);
counters.Add(performanceCounter);
}
public static PerformanceCounter InstantiateCounter(string categoryName, string counterName)
{
PerformanceCounter performanceCounter;
performanceCounter = new PerformanceCounter(categoryName, counterName);
performanceCounter.MachineName = ".";
performanceCounter.ReadOnly = false;
performanceCounter.RawValue = 0;
return performanceCounter;
}
}
Section 4: Encapsulating the fields
Our objective is to ease the life of the developer and provide him good control of the application. But the individual performance counters are not accessible. We will need to give the user of class access to individual counter. Hence we will refactor and encapsulate them to properties. The advantage of this will be we will no longer need to remember the hard coded names.
/// <summary>
/// Counter for counting total number of operations
/// </summary>
private PerformanceCounter _TotalOperations;
public PerformanceCounter TotalOperations
{
get { return _TotalOperations; }
set { _TotalOperations = value; }
}
/// <summary>
/// Counter for counting number of operations per second
/// </summary>
private PerformanceCounter _OperationsPerSecond;
public PerformanceCounter OperationsPerSecond
{
get { return _OperationsPerSecond; }
set { _OperationsPerSecond = value; }
}
/// <summary>
/// Counter for counting duration averages
/// </summary>
private PerformanceCounter _AverageDuration;
public PerformanceCounter AverageDuration
{
get { return _AverageDuration; }
set { _AverageDuration = value; }
}
/// <summary>
/// Counter for counting duration averages base
/// </summary>
private PerformanceCounter _AverageDurationBase;
public PerformanceCounter AverageDurationBase
{
get { return _AverageDurationBase; }
set { _AverageDurationBase = value; }
}
Section 5: Provide further control to the users of our class
Though the constructor does the work of creating and instantiating the performance counters, we have no control over the process. Hence our next step is to give more control and power to the user of our class.
So we will remove the call to ‘Create’ and ‘Instantiate’ methods from the constructor.
Parameterized Constructor
Also we have been hard coding the category so we will have a parameterized constructor.
/// <summary>
/// Creates a new performance counter category _CategoryName if it does not already exists and adds some counters to it.
/// </summary>
public PerformanceCounters(string categoryName)
{
_CategoryName = categoryName;
}
Modify the access modifiers
We will also modify the access modifiers of ‘Create’ and ‘Instantiate’ to public. Hence our methods will look like this.
public void Instantiate()
{
// create counters to work with
_TotalOperations = PerformanceCountersHelpers.InstantiateCounter(_CategoryName, "# operations executed");
_OperationsPerSecond = PerformanceCountersHelpers.InstantiateCounter(_CategoryName, "# operations / sec");
_AverageDuration = PerformanceCountersHelpers.InstantiateCounter(_CategoryName, "average time per operation");
_AverageDurationBase = PerformanceCountersHelpers.InstantiateCounter(_CategoryName, "average time per operation base");
}
public void Create()
{
if (PerformanceCounterCategory.Exists(_CategoryName))
PerformanceCounterCategory.Delete(_CategoryName);
if (!PerformanceCounterCategory.Exists(_CategoryName))
{
CounterCreationDataCollection counters = new CounterCreationDataCollection();
// 1. counter for counting totals: PerformanceCounterType.NumberOfItems32
PerformanceCountersHelpers.CreateCounter(counters, "# operations executed", "Total number of operations executed", PerformanceCounterType.NumberOfItems32);
// 2. counter for counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
PerformanceCountersHelpers.CreateCounter(counters, "# operations / sec", "Number of operations executed per second", PerformanceCounterType.RateOfCountsPerSecond32);
// 3. counter for counting average time per operation: PerformanceCounterType.AverageTimer32
PerformanceCountersHelpers.CreateCounter(counters, "average time per operation", "Average duration per operation execution", PerformanceCounterType.AverageTimer32);
// 4. base counter for counting average time per operation: PerformanceCounterType.AverageBase
PerformanceCountersHelpers.CreateCounter(counters, "average time per operation base", "Average duration per operation execution base", PerformanceCounterType.AverageBase);
// create new category with the counters above
PerformanceCounterCategory.Create(_CategoryName, "Sample category for Codeproject", counters);
}
}
We will provide a Delete method. Please note that the entire category can be deleted and not individual performance counter.
public void Delete()
{
if (PerformanceCounterCategory.Exists(_CategoryName))
PerformanceCounterCategory.Delete(_CategoryName);
}
We will also rename PerformanceCounterSample class to PerformanceCounters.
Section 4: Move the processing outside the class
We have given a lot of power to the user of our class. But we still are doing the processing using DoSomeProcessing in the class.
/// <summary>
/// Increments counters.
/// </summary>
/// <param name="ticks">The number of ticks the AverageTimer32 counter must be incremented by</param>
public void DoSomeProcessing(long ticks)
{
// simply increment the counters
_TotalOperations.Increment();
_OperationsPerSecond.Increment();
_AverageDuration.IncrementBy(ticks); // increment the timer by the time cost of the operation
_AverageDurationBase.Increment(); // increment base counter only by 1
}
The user of our class should now decide what processing he wants to do. So it is the right time to move this code out of the class.
PerformanceCounterSampleStarter is now modified and moved to a separate file. It contains ‘DoSomeProcessing’ method.
class PerformanceCounterSampleStarter
{
/// <summary>
/// Imports the <code>QueryPerformanceFrequency</code> method into the class. The method is used to measure the current
/// tickcount of the system.
/// </summary>
/// <param name="ticks">current tick count</param>
[DllImport("Kernel32.dll")]
public static extern void QueryPerformanceCounter(ref long ticks);
/// <summary>
/// Main thread
/// </summary>
[STAThread]
static void Main(string[] args)
{
PerformanceCounterSample test = new PerformanceCounterSample("MyCategory");
//The create method needs to be called once in the entire application
test.Create();
//Instantiate can be called any time, in different classes or scope in the application.
//Create has to be called before instantiate
test.Instantiate();
Random rand = new Random();
long startTime = 0;
long endTime = 0;
for (int i = 0; i < 1000; i++)
{
// measure starting time
QueryPerformanceCounter(ref startTime);
System.Threading.Thread.Sleep(rand.Next(500));
// measure ending time
QueryPerformanceCounter(ref endTime);
// do some processing
DoSomeProcessing(endTime - startTime, test);
}
}
/// <summary>
/// Increments counters.
/// </summary>
/// <param name="ticks">The number of ticks the AverageTimer32 counter must be incremented by</param>
public static void DoSomeProcessing(long ticks, PerformanceCounterSample perfCounterSample)
{
// simply increment the counters
perfCounterSample.TotalOperations.Increment();
perfCounterSample.OperationsPerSecond.Increment();
perfCounterSample.AverageDuration.IncrementBy(ticks); // increment the timer by the time cost of the operation
perfCounterSample.AverageDurationBase.Increment(); // increment base counter only by 1
}
}
Section 5: TicksCounter class – A helper class to measure ticks
Developers using our class will have to do some extra effort to calculate the ticks for average. Hence we will ease his life and make provision to measure the ticks for the average we will create a TicksCounter class.
public class TicksCounter
{
/// <summary>
/// Imports the QueryPerformanceFrequency method into the class. The method is used to measure the current
/// tickcount of the system.
/// </summary>
/// <param name="ticks">current tick count</param>
[DllImport("Kernel32.dll")]
public static extern void QueryPerformanceCounter(ref long ticks);
private long _startTime = 0;
private long _endTime = 0;
/// <summary>
/// Starts the ticks count.
/// </summary>
public void Start()
{
QueryPerformanceCounter(ref _startTime);
}
/// <summary>
/// Stops the ticks count.
/// </summary>
public void Stop()
{
QueryPerformanceCounter(ref _endTime);
}
/// <summary>
/// Gets the ticks.
/// </summary>
/// <value>The ticks.</value>
public long Ticks
{
get { return _endTime - _startTime; }
}
}
Hence our main method will look like this.
[STAThread]
static void Main(string[] args)
{
PerformanceCounters test = new PerformanceCounters("TestCategory1");
//The create method needs to be called once in the entire application
test.Create();
//Instantiate can be called any time, in different classes or scope in the application.
//Create has to be called before instantiate
test.Instantiate();
TicksCounter ticksCounter = new TicksCounter();
for (int i = 0; i < 1000; i++)
{
ticksCounter.Start();
System.Threading.Thread.Sleep(rand.Next(500));
// measure ending time
ticksCounter.Stop();
// do some processing
DoSomeProcessing(ticksCounter.Ticks, test);
}
}
Review
Finally let us review what we have achieved so far. We started with the following classes.

Note: The source code of the old solution is provided
PerformanceCountersSample has following structure:

Note: The source code of the final solution is provided
1) PerformanceCounters class
· PerformanceCounterSample is renamed to PerformanceCounters.
· Methods Create, Delete, Instantiate are provided to create, delete, instantiate counters.
· Properties are provided to have more access to individual performance counters


Ticks counter class to calculate the ticks for average

In this article, I have revisited the Performance Counters solution and thus improved the design of an existing code base.
