1

I know there are several answers here on SO on how to measure CPU usage with either of two approaches:

For some days now I am miserably failing to perform CPU usage measurements of my program with either of these - with both mechanisms I get a CPU usage that is smaller than displayed in Task Manager or Process Explorer. Is there some magic how these tools do this and is this related to HyperThreading being enabled? I will perform my tests on a CPU without HyperThreding but if anyone can point out what am I missing here I would be very thankful.

To illustrate what I have tried, here is the code that does PDH based measruements:

class CCpuUsageMonitor
{
public:
    CCpuUsageMonitor(const wchar_t* pProcessName)
    {

        GetSystemInfo(&m_SystemInfo);
        auto nStatus = PdhOpenQuery(NULL, NULL, &m_hPdhQuery);
        _ASSERT(nStatus == ERROR_SUCCESS);
        nStatus = PdhAddCounter(m_hPdhQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &m_hPdhCpuUsageCounter);
        _ASSERT(nStatus == ERROR_SUCCESS);
        wchar_t pCounterPath[PDH_MAX_COUNTER_PATH];
        StringCbPrintf(pCounterPath, PDH_MAX_COUNTER_PATH, L"\\Process(%s)\\%% Processor Time", pProcessName);
        nStatus = PdhAddCounter(m_hPdhQuery, pCounterPath, NULL, &m_hPhdProcessCpuUsageCounter);
        _ASSERT(nStatus == ERROR_SUCCESS);
    }

    ~CCpuUsageMonitor()
    {
        PdhCloseQuery(&m_hPdhQuery);
    }

    void CollectSample()
    {
        auto nStatus = PdhCollectQueryData(m_hPdhQuery);
        _ASSERT(nStatus == ERROR_SUCCESS);
    }

    double GetCpuUsage()
    {
        DWORD nType;
        PDH_FMT_COUNTERVALUE CounterValue;
        auto nStatus = PdhGetFormattedCounterValue(m_hPdhCpuUsageCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &nType, &CounterValue);
        _ASSERT(nStatus == ERROR_SUCCESS);
        return CounterValue.doubleValue;
    }

    double GetProcessCpuUsage()
    {
        DWORD nType;
        PDH_FMT_COUNTERVALUE CounterValue;
        auto nStatus = PdhGetFormattedCounterValue(m_hPhdProcessCpuUsageCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &nType, &CounterValue);
        _ASSERT(nStatus == ERROR_SUCCESS);
        return CounterValue.doubleValue / m_SystemInfo.dwNumberOfProcessors;
    }

private:
    SYSTEM_INFO m_SystemInfo;
    HANDLE m_hPdhQuery;
    HANDLE m_hPdhCpuUsageCounter;
    HANDLE m_hPhdProcessCpuUsageCounter;
};

With the second approach I basically take two snapshots of process times via GetProcessTimes() before and after my code runs, substract and divide against wall time multiplied by the number of processors.

Rudolfs Bundulis
  • 11,636
  • 6
  • 33
  • 71
  • How much difference do you get? Sampling error, or "completely different"? – Mats Petersson Nov 25 '15 at 19:35
  • @MatsPetersson a constanly lower measurement by a factor of around 1.5 to 2. The test code is doing spurious wakeups from a timer and working for short regular periods of time. Maybe the tools actually sample at high time interval and are wrong? I'll try to make a complete example. – Rudolfs Bundulis Nov 25 '15 at 20:01
  • @RudolfsBundulis: Hey, I'm curious if you got it figured out? I'm dealing with the same issue. Want to exchange ideas if you're still on it? – c00000fd Feb 08 '17 at 02:24
  • @c00000fd If honestly this was very long age, I don't remember if I came to a fix or no. This code was basically to test H.264 encoding for a paper in the university but I do not remember if I discarded the CPU measurements in the end or no. The code is available at https://github.com/rubu/AIEEE2015/blob/master/Shared/TestRun.hpp, so you can simply try if it works better than what is pasted here or has the same issue:D Sorry for that:) – Rudolfs Bundulis Feb 08 '17 at 12:48

2 Answers2

1

Here are a few links I've used in the past and a good article on why GetThreadTimes is wrong (I wouldn't use it as a reliable source of data):

http://blog.kalmbachnet.de/?postid=28

https://msdn.microsoft.com/en-us/library/aa392397(VS.85).aspx

http://www.drdobbs.com/windows/win32-performance-measurement-options/184416651

https://msdn.microsoft.com/en-us/library/aa394279(VS.85).aspx

You seem well on your way and knowledgeable those links should get you going in the right direction at least.

MikeL
  • 59
  • 2
0

From this link:

Starting with Windows 8, a change was made to the way that Task Manager and Performance Monitor report CPU utilization...

This change affects the way that CPU utilization is computed. The values in Task Manager now correspond to the Processor Information\% Processor Utility and Processor Information\% Privileged Utility performance counters, not to the Processor Information\% Processor Time and Processor Information\% Privileged Time counters as in Windows 7.

Your code will work as written other than the change in which counters you are querying. You are using the Processor counters; you should switch to Processor Information enabled in Windows 8; and also use the "Utility" versions of the counters.

If you query the formatted value as you currently do, you'll get the same number displayed on the Task manager with 1-second polling.

If you want to do calculations over longer intervals, you can query the raw value; the numbers into a PDH_RAW_COUNTER structure instead of your current PDH_FMT_COUNTERVALUE. The values used to calculate the usage for the numerator are in the PDH_RAW_COUNTER structure's FirstValue, and the "base" values for the denominator are in SecondValue.

Daniel Widdis
  • 8,424
  • 13
  • 41
  • 63