CPU and disk usage values which we get from windows API (Using PDH) is not same as the value we see in task manager

Chandan M S 20 Reputation points
2024-12-05T15:51:07.8166667+00:00

I am currently using the PDH (Performance Data Helper) API to log CPU and Disk utilization data in a CSV file. Below is a snippet of the code I am using:

#include <windows.h>
#include <pdh.h>
#include <stdio.h>
#include <time.h>

#pragma comment(lib, "Pdh.lib")

void logCpuAndDiskUtilization() {
    PDH_HQUERY cpuQuery, diskQuery;
    PDH_HCOUNTER cpuTotal, diskRead, diskWrite, diskTime;
    PDH_FMT_COUNTERVALUE cpuCounterVal, diskReadVal, diskWriteVal, diskTimeVal;
    PDH_STATUS status;
    FILE* file;
    errno_t err;

    err = fopen_s(&file, "C:\\CPU\\cpu_disk_data.csv", "w");
    if (err != 0) {
        printf("Failed to open file for writing\n");
        return;
    }

    fprintf(file, "Timestamp,CPU Utilization,Disk Read Time,Disk Write Time,Disk Time\n");

    status = PdhOpenQuery(NULL, 0, &cpuQuery);
    if (status != ERROR_SUCCESS) {
        printf("PdhOpenQuery for CPU failed with status 0x%x\n", status);
        fclose(file);
        return;
    }

    status = PdhOpenQuery(NULL, 0, &diskQuery);
    if (status != ERROR_SUCCESS) {
        printf("PdhOpenQuery for Disk failed with status 0x%x\n", status);
        PdhCloseQuery(cpuQuery);
        fclose(file);
        return;
    }

    PdhAddCounter(cpuQuery, TEXT("\\Processor(_Total)\\% Processor Time"), 0, &cpuTotal);
    PdhAddCounter(diskQuery, TEXT("\\PhysicalDisk(0 C:)\\% Disk Read Time"), 0, &diskRead);
    PdhAddCounter(diskQuery, TEXT("\\PhysicalDisk(0 C:)\\% Disk Write Time"), 0, &diskWrite);
    PdhAddCounter(diskQuery, TEXT("\\PhysicalDisk(0 C:)\\% Disk Time"), 0, &diskTime);

    PdhCollectQueryData(cpuQuery);
    PdhCollectQueryData(diskQuery);

    int i = 60;
    while (i > 0) {
        Sleep(1000); // Allow some time to collect data
        i--;
        printf("%d", i);

        PdhCollectQueryData(cpuQuery);
        PdhCollectQueryData(diskQuery);

        PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &cpuCounterVal);
        PdhGetFormattedCounterValue(diskRead, PDH_FMT_DOUBLE, NULL, &diskReadVal);
        PdhGetFormattedCounterValue(diskWrite, PDH_FMT_DOUBLE, NULL, &diskWriteVal);
        PdhGetFormattedCounterValue(diskTime, PDH_FMT_DOUBLE, NULL, &diskTimeVal);

        fprintf(file, "%ld,%.2f,%.2f,%.2f,%.2f\n", time(NULL), cpuCounterVal.doubleValue, diskReadVal.doubleValue, diskWriteVal.doubleValue, diskTimeVal.doubleValue);
        fflush(file); // Ensure data is written to the file
    }

    PdhCloseQuery(cpuQuery);
    PdhCloseQuery(diskQuery);
    fclose(file);
}

int main() {
    logCpuAndDiskUtilization();
    return 0;
}

However, I have noticed that the values I get from this code do not match the values shown in the Task Manager. Specifically, the CPU and Disk utilization percentages are different. I have posted images of graphs over a 60-second period that show the differences between the values obtained using the PDH API and those displayed in the Task Manager. You can find these images attached to this post. Could you please help me understand why there is a discrepancy between the values obtained using the PDH API and those displayed in the Task Manager? Is there a different API or method that Task Manager uses to get these values? Any guidance on how to achieve more accurate readings would be greatly appreciated.

enter image description hereCPU_utilization_1

Windows development Windows API - Win32
Windows for business Windows Client for IT Pros Devices and deployment Other
Windows for business Windows Client for IT Pros User experience Other
Developer technologies C++
0 comments No comments
{count} votes

Accepted answer
  1. Michael Taylor 60,161 Reputation points
    2024-12-05T16:17:03.06+00:00

    Trying to sync Task Manager, and many other tools, with each other is difficult at best. The issues range from using different APIs (e.g. PDH vs native API calls) to meaning. For example is the memory usage private bytes, working set, or something else? Does it include shared memory or just process memory? Thus trying to compare results across toolsets is not terribly useful. In general you should limit comparisons to within the toolset. For example if process A is using 10% more memory than process B in Task Manager then you might expect a similar relationship using another tool, like Process Explorer, but that depends on what "memory" they are looking at. It's relative.

    There are quite a few articles written over the years comparing the various ways to measure memory and how different tools do it. Here's one such link that is a little old but still relevant. Here's some more links on SO and yet another link. Again, at the end of the day, either stick with a single tool and treat the values as relative to each other or you're going to run into issues.

    As for your actual question, if you're using PDH then that should be fine. It might not line up with Task Manager but Task Manager is designed for a more general use case so it may aggregate differently. TM most likely aggregates related values together to produce a simplified view whereas the counters are more fine grain. You might consider looking at Process Explorer instead. It has more fine grain output as well and likely more closely matches what you're seeing in PDH. You can then choose to aggregate where you see fit.

    If you are not happy with the PDH stuff then you can look into using other options. The Event Tracing API may be useful for CPU tracking. They even have a sample for calculating CPU usage. Another option, if you want to line up with TM (or at least it used to) more closely is to use NtQuerySystemInformation. But that function is deprecated so you'll need to use different functions now.

    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.