Subscribe to Martyr2's Programming Underground        RSS Feed
***** 1 Votes

Using Performance Counters in the C# Language

Icon Leave Comment
At any given time the Windows operating system is tracking statistics for the system and many of the processes / applications that are currently running on it. Things like the number of processors, how many threads are executing, how often the CLR is in garbage collection, the number of I/O operations being performed etc are all tracked through things called performance counters. One way you can look at these various counters is to look at them through the Performance Monitor program... also known as perfmon.exe. If you go to the Start button (in Windows 7 or similar versions of Windows) and in the run box type "perfmon.exe" you should see Performance Monitor pop up. There you can explore the various counters available to you.

The Structure of Performance Counters and Categories

In many texts you may have read, they always structure the system as a list of categories (like System, Server, Processor, .NET CLR Memory etc) and within those categories you have similar counters grouped together. For instance, in the Processor category you would have counters related to information about your CPU and its cores. One thing I find most texts don't do is explain a straight forward relationship with the idea of "instances". In order to read "most" counters you have to have an instance for a given category. So instead of being a category, counter and then various instances it is more like category, zero or more instances for which the counters of that category can be applied. This means a counter category can have counters which, if no instances exist for that type of category, are relatively unusable at any given point in time.

As an example let's take the performance category .NET CLR Memory. This category provides counters which are related to .NET applications and their CLR memory statistics. If I am running my .NET test application I would see an instance of the exe pop up in this .NET CLR Memory category. I could then be able to apply counters to it to see how much time it spends garbage collecting objects, the number of handles it uses, the number of Gen 0 collections etc. But if I didn't have any instances running, this category would not be much use. I wouldn't have an instance to apply counters to. Categories like Processor should always have instances because it applies to your CPU which surely you have one or more of. If you are running a quad core computer, you would see 4 instances there, each one has the same set of counters being applied to them.

The reason I am explaining this is that in our code below we have to check for instances before we attempt to read some counters. Otherwise we can run into trouble in that we are trying to read the performance of an instance that is not at all performing anything.

Why Would We Need Performance Counters

The information tied up in one of these counters can give us insight into our applications and the system for which your program might be running. We could check if there is enough available memory, we could check if the computer is using multiple CPU cores, we can tell if perhaps our app is using too much resources and free up some things etc. With these counters we can gauge the overall health of our application and the system as a whole. And before you ask, yes you can reach this type of information using other means like the Windows Management Instrumentation (WMI) system or through some native .NET objects. However counters can be one mechanism for gauging the current runtime stats and reach many things that may be either impossible or harder to read using other methods.

Reading a Counter Using C#

Before we start with our code, try opening up Performance Monitor now and browse through the various categories and counters. For our code we are going to choose a simple one that will show you how things work. Our example will read "Bytes Received/sec" of our main active network interface connection (NIC). We will set it up to poll the NIC for our stat every second for twenty seconds. Since our connections are typically always receiving some kind of info these days, you should see numbers almost immediately. If not, you can just open up a browser while this is running and surf to a website to then start seeing the numbers change.

using (PerformanceCounter pc = new PerformanceCounter("Network Interface", "Bytes Received/sec", "Realtek RTL8174C[P]_8111C Family PCI-E Gigabit Ethernet NIC")) {
    int i = 0;
    while (i < 20) {
        Console.WriteLine(pc.NextValue());
        i++;
        Thread.Sleep(1000);
    }
}



Reading performance counters involves using the PerformanceCounter class which you can find in the System.Diagnostics namespace. It takes the category, the counter name and optionally an instance. In our example we are specifying the Network Interface category, we are going to want to see the Bytes Received/sec count of our active NIC... the instance. To pull a value from the counter we are using the NextValue() method and printing it to the console window so we can see what is being displayed. We then sleep for a second and go back to taking our next reading. This particular method isn't ideal since if you were to drop this in a form since you would end up blocking your main thread for 20 seconds, but it is great as a simple explanation.

From this structure you can see that these counters are made to be polled over and over again to form things like a chart, which is why you see such a chart in performance monitor. Mixing this idea with something like a monitoring background thread and you have a setup for reading statistical data while the user does something else.

Creating Counters and Writing Data to Them

Perhaps you have your own statistics you want to keep at some given time for your application. You can create such counters programmatically yourself using the CounterCreationDataCollection. Here we add new counters by creating an instance of CounterCreationData(), specifying the counter name, help info and the underlying counter type (like if it is NumberOfItems32). Once we have added all our counters to the collection, we pass the category name and collection to the PerformanceCounterCategory.Create() method to create it.

Note: You may need admin privileges at this point to add the category. Normally you wouldn't want to add your own category unless you were initially setting up an program. At this point you would get the proper permissions and then create the counters. I find that most of the time all I ever really need to do is read existing counters.

String ourCategory = "Example Counter Category";

// Check if the category already exists first
if (!PerformanceCounterCategory.Exists(ourCategory)) {
    CounterCreationDataCollection col = new CounterCreationDataCollection();

    // Add a couple of counters here to the collection
    col.Add(new CounterCreationData("Counter 1", "This is Counter 1", PerformanceCounterType.NumberOfItems32));
    col.Add(new CounterCreationData("Counter 2", "This is Counter 2", PerformanceCounterType.AverageTimer32));
                
    // Add the base counter.
    CounterCreationData averageTimer32Base = new CounterCreationData();
    averageTimer32Base.CounterType = PerformanceCounterType.AverageBase;
    averageTimer32Base.CounterName = "AverageTimer32SampleBase";
    col.Add(averageTimer32Base);

    // Add our collection to the category, but requires admin privileges.
    PerformanceCounterCategory.Create(ourCategory, "Example Counters", PerformanceCounterCategoryType.SingleInstance, col);
}



From our code above you can see how the steps progress from checking if the category already exists through to creating the category. If a category exists, you are going to have to delete the category first before you can create it again (using the PerformanceCounterCategory.Delete method). So the code above may also include some sort of else statement that can delete the category prior to creating the collection.

Lastly, Manipulating Your Created Counters

This part is pretty straight forward. Once you have created your custom counters, you just have to use the PerformanceCounter class again to read the counter, but set its "ReadOnly" property to false so that you can set its .RawValue property to some value. You can also increment the value by using methods like Increment() or IncrementBy() to increment it by some fixed amount. Again, make sure you have the proper admin privileges first if you are running into a wall here.

using (PerformanceCounter perfCounter = new PerformanceCounter(cat, counterName, "")) {
   perfCounter.ReadOnly = false;
   perfCounter.RawValue = 1;
   
   // Increment the counter by 100 (so it will be 101)
   perfCounter.IncrementBy(100);
}



Some Gotchas To Avoid

While playing with counters you may run into a couple strange problems and I just wanted to make sure you were aware of them. One of the first ones you may run into is involving the reading of a specific counter. The counter may initially come out zero at first. This is because counters are based off of reading a previous value to calculate its current value. Since there is no previous value on the first sample, it comes out zero. The next value pull should then start having a value.

Another gotcha, which we briefly touched on, was that in order to read and manipulate some counters you are going to have to have admin privileges. I found this really only came into play when it came to creating counter categories. Usually to read any of the values you don't run into any problems.

If you are having problems reading a category or counter, make sure you spelled everything right and proper capitalization. The names can be a bit tricky and make sure everything is exact. Use performance monitor to help get the category names and counters right so that you are not hitting your head against the wall with this one.

Last gotcha, while enumerating the list of categories and counters, be careful. Some of the lists can get really long and take a long time to list. We are talking about some of the categories listing thousands of lines of statistical data! If something seems to be frozen for a few seconds, be patient.

I hope you enjoy working with the performance counters and that you are able to use this information in your next project. Let us all know what you have used performance counters for in our comments below so we shall all bask in the glory of your "leetness".

Don't forget to purchase our ebook which contains over 200 miscellaneous project ideas which contains projects for all levels and languages.

Thanks for reading! :)

0 Comments On This Entry

 

September 2014

S M T W T F S
 123456
78910111213
14151617181920
2122 23 24252627
282930