1

We have created a monitoring application for our enterprise app that will monitor our applications Performance counters. We monitor a couple system counters (memory, cpu) and 10 or so of our own custom performance counters. We have 7 or 8 exes that we monitor, so we check 80 counters every couple seconds.

Everything works great except when we loop over the counters the cpu takes a hit, 15% or so on my pretty good machine but on other machines we have seen it much higher. We are wanting our monitoring app to run discretely in the background looking for issues, not eating up a significant amount of the cpu.

This can easily be reproduced by this simple c# class. This loads all processes and gets Private Bytes for each. My machine has 150 processes. CallNextValue Takes 1.4 seconds or so and 16% cpu

class test
{
    List<PerformanceCounter> m_counters = new List<PerformanceCounter>();

    public void Load()
    {
        var processes = System.Diagnostics.Process.GetProcesses();
        foreach (var p in processes)
        {
            var Counter = new PerformanceCounter();
            Counter.CategoryName = "Process";
            Counter.CounterName = "Private Bytes";
            Counter.InstanceName = p.ProcessName;

            m_counters.Add(Counter);
        }
    }

    private void CallNextValue()
    {
        foreach (var c in m_counters)
        {
            var x = c.NextValue();              
        }
    }
}

Doing this same thing in Perfmon.exe in windows and adding the counter Process - Private Bytes with all processes selected I see virtually NO cpu taken up and it's also graphing all processes.

So how is Perfmon getting the values? Is there a better/different way to get these performance counters in c#?

I've tried using RawValue instead of NextValue and i don't see any difference.

I've played around with Pdh call in c++ (PdhOpenQuery, PdhCollectQueryData, ...). My first tests don't seem like these are any easier on the cpu but i haven't created a good sample yet.

Scott
  • 21
  • 4
  • are you running this in a while(1) or how often this is called CallNextValue()? When i had same problem i used threads to divide it using tpm. another solution is which is not very good but works for reducing the CPU % is put a Thread.sleep(5) after each iteration don't run the loop as fast as it can – sumeet kumar Aug 19 '15 at 21:47
  • In our monitoring app we pull the counters every 5 seconds. In this test app i just hooked a button up to call CallNextValue and it always has the same result, 16% cpu or so. – Scott Aug 19 '15 at 21:51

2 Answers2

0

I'm not very familiar with the .NET performance counter API, but I have a guess about the issue.

The Windows kernel doesn't actually have an API to get detailed information about just one process. Instead, it has an API that can be called to "get all the information about all the processes". It's a fairly expensive API call. Every time you do c.NextValue() for one of your counters, the system makes that API call, throws away 99% of the data, and returns the data about the single process you asked about.

PerfMon.exe uses the same PDH APIs, but it uses a wildcard query -- it creates a single query that gets data for all of the processes at once, so it essentially only calls c.NextValue() once every second instead of calling it N times (where N is the number of processes). It gets a huge chunk of data back (data for all of the processes), but it's relatively cheap to scan through that data.

I'm not sure that the .NET performance counter API supports wildcard queries. The PDH API does, and it would be much cheaper to perform one wildcard query than to perform a whole bunch of single-instance queries.

0

Sorry for a long response, but I've found your question only now. Anyway, if anyone will need additional help, I have a solution:

I've made a little research on my custom process and I've understood that when we have a code snippet like

    PerformanceCounter ourPC = new PerformanceCounter("Process", "% Processor time", "processname", true);
    ourPC.NextValue();

Then our performance counter's NextValue() will show you the (number of logical cores * task manager cpu load of the process) value which is kind of logical thing, I suppose.

So, your problem may be that you have a slight CPU load in the task manager because it understands that you have a multiple core CPU, although the performance counter counts it by the formula above.

I see a one (kind of crutchy) possible solution for your problem so your code should be rewritten like this:

private void CallNextValue()
{
    foreach (var c in m_counters)
    {
        var x = c.NextValue() / Environment.ProcessorCount;              
    }
}

Anyway, I do not recommend you to use Environment.ProcessorCount although I've used it: I just didn't want to add too much code to my short snippet.

You can see a good way to find out how much logical cores (yeah, if you have core i7, for example, you'll have to count logical cores, not physical) do you have in a system if you'll follow this link:

How to find the Number of CPU Cores via .NET/C#?

Good luck!

Roman Krylov
  • 49
  • 1
  • 11