Wednesday, July 17, 2013

Get CPU Usage Across All Cores In C# Using WMI

There are (2) main ways I have seen to get the CPU usage in a .NET app: via a PerformanceCounter and via WMI. The PerformanceCounter code seemed ideal and is the most concise, but had a caveat for this particular (CPU) counter. The code to retrieve this value using this method is as follows:
//Getting the CPU usage via a PerformanceCounter
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
System.Threading.Thread.Sleep(1000); // wait a second to get a valid reading
var usage = cpuCounter.NextValue();

Notice that stall for 1 second nested in that code above? As documented here: Retrieving Accurate CPU Usage In C# and as seen in almost all code examples, you must add in that stall in order to get an accurate reading. It appears the 1 second value was not arbitrary either and is required in order for the reading to refresh the value.

Well how many are using this statistic in a static manner that is only shown 1 time ever? Not anyone I'm guessing, which mean this code is a part of a page reload or top level refresh in your app that is occurring often. Then I see some code smell because I don't want to have to wait 1 second every time my main page/form/etc. loads. Thinking async? Might work in a WinForm/WPF situation where this could occur on a separate thread, but if this is a part of a web app say monitoring a server, you could incur this hit on every postback. Also, if the total of processing is still having to wait on this code and everything else is complete in  less than 1 second, you will still be delayed in net time of 1 second. Point being, incorporate this code into a separate async process or deal with a 1 second delay for every time called.

I think the better solution here is just to use an alternative method in WMI. I also like this method because you get an array/list back with the reading from each core and then the total from all cores. The following WMI code snippet I found can be used to get the CPU core usage values:

//Get CPU usage values using a WMI query
ManagementObjectSearcher searcher = new ManagementObjectSearcher("select * from Win32_PerfFormattedData_PerfOS_Processor");
            var cpuTimes = searcher.Get()
                .Select(mo => new
                    Name = mo["Name"],
                    Usage = mo["PercentProcessorTime"]

The thing is I didn't really know what to do with this information at 1st until I dug into it. The List returned will contain the current CPU usage for each core on the processor (1..n) and the total average of all cores. This is the value I'm interested in, and is represented as the last item in the List returned with the name "_Total". Here is the remaining code to get the CPU usage as a single value:

//The '_Total' value represents the average usage across all cores,
//and is the best representation of overall CPU usage
var query = cpuTimes.Where(x => x.Name.ToString() == "_Total").Select(x => x.Usage);
var cpuUsage = query.SingleOrDefault();

If you want to verify the values, add up each core's value in the cpuTimes List and then divide by the total number of cores to get the "_Total" value.