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 0 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
Windows
A family of Microsoft operating systems that run across personal computers, tablets, laptops, phones, internet of things devices, self-contained mixed reality headsets, large collaboration screens, and other devices.
5,699 questions
Windows API - Win32
Windows API - Win32
A core set of Windows application programming interfaces (APIs) for desktop and server applications. Previously known as Win32 API.
2,704 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,818 questions
Windows Hardware Performance
Windows Hardware Performance
Windows: A family of Microsoft operating systems that run across personal computers, tablets, laptops, phones, internet of things devices, self-contained mixed reality headsets, large collaboration screens, and other devices.Hardware Performance: Delivering / providing hardware or hardware systems or adjusting / adapting hardware or hardware systems.
1,657 questions
Windows 11
Windows 11
A Microsoft operating system designed for productivity, creativity, and ease of use.
10,399 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Michael Taylor 55,841 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

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.