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");
cpuCounter.NextValue();
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()
                .Cast<managementobject>()
                .Select(mo => new
                {
                    Name = mo["Name"],
                    Usage = mo["PercentProcessorTime"]
                }
                )
                .ToList();

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.


14 comments:

  1. This looks cool in theory, unfortunately the code doesn't work. The line ".Cast()" indicates managementobject isn't recognized.. and even if I capitalize it properly, ie ManagementObject, visual studio shows this error: "'System.Management.ManagementObjectCollection' does not contain a definition for 'Cast' and the best extension method overload 'System.Data.EnumerableRowCollectionsExtensions.Cast(System.Data.EnumerableRowCollection)' has some invalid arguments"

    Otherwise it looked like a neat idea..

    ReplyDelete
  2. You will need to add a reference to System.Linq because the .Cast method on an enumerable is in System.Linq.Enumerable.Cast<TResult> Add using System.Linq; and that code should be resolved.

    ReplyDelete
  3. Ohh of course, I figured it would be something like a missing reference; thanks :)

    ReplyDelete
  4. Thanks for that one. much better without that "sleep" :-)
    I'm not a real programmer, just able to tweak existing codes to do things I need.
    I looked around how to do the same thing (WMI) for network bandwidth utilization, but no change I did worked.

    If you can, and know how, can you please post how to change your code in order to get network bandwidth thru WMI?
    If its easy to get "total" and "per process" that would be super.

    Thanks.
    TC

    ReplyDelete
  5. @TC - I don't have the code written to use WMI to capture network bandwidth, but I found a link containing some samples and additional links that may help you: http://social.msdn.microsoft.com/Forums/en-US/7bc5c776-88af-4479-ba35-2ef43cebf4ee/logic-to-monitor-internet-bandwidth-usage?forum=csharpgeneral

    ReplyDelete
  6. Hi folks,
    look into stackoverflow
    http://stackoverflow.com/questions/9777661/returning-cpu-usage-in-wmi-using-c-sharp
    You were very close to the correct solution. Thanks for the idea!

    ReplyDelete
  7. Hi Allen,
    you were very close to usable soltion.
    Look here:
    http://stackoverflow.com/questions/9777661/returning-cpu-usage-in-wmi-using-c-sharp

    Thanks for the idea

    ReplyDelete
  8. Hi Allen,
    Is there a way to view this information for a particular application instead of all processes.
    Thanks

    ReplyDelete
  9. Hi Allen,

    Thanks for posting this. Perfect solution for what I needed.

    Best,
    Dax

    ReplyDelete
  10. Some time later... What about disposal of the management objects? Is this not required as they implement IDisposable. How about the following:

    public static int TotalUsage
    {
    get
    {

    //Get CPU usage values using a WMI query

    using (var searcher = new ManagementObjectSearcher("select PercentProcessorTime from Win32_PerfFormattedData_PerfOS_Processor where Name='_Total'"))
    {
    using (var moc = searcher.Get())
    {
    var usage = int.Parse(
    moc
    .Cast()
    .FirstOrDefault()
    .GetPropertyValue("PercentProcessorTime").ToString()
    );
    return usage;
    }
    }
    }
    }

    ReplyDelete
  11. Can we have a vb.net code for this?

    Thank you,

    ReplyDelete
  12. Hi,
    Thanx for the post
    I have gone through above mentioned methods but the value return by PerformanceCounter(NextValue()) and WMI(PercentProcessorTime)
    is not matching with the Task Manager , There might be some sort of calculation needed to get the value like TaskManager for Indivual process.
    Can please help..

    read the 3rd comment fromm below ref link, But i dont know what thet are saying
    https://social.technet.microsoft.com/Forums/en-US/0435e7c5-3cda-41a0-953e-7fa462fde03b/perfmon-process-processor-time-vs-task-managers-cpu-usage-for-monitoring-a-specific-user?forum=perfmon

    ReplyDelete
  13. running this code in a winforms app targetting .NET 5 I get the following error:

    System.Management currently is only supported for Windows desktop applications

    ReplyDelete