使用 C++ 在 Windows 中检索 CPU 负载百分比总数

Posted

技术标签:

【中文标题】使用 C++ 在 Windows 中检索 CPU 负载百分比总数【英文标题】:Retrieving CPU Load Percent total in Windows with C++ 【发布时间】:2014-06-02 08:20:42 【问题描述】:

我一直在开发这个工具来快速记录一些系统统计信息,例如内存信息和cpu 负载百分比(例如任务管理器中显示的内容)。我似乎已经处理了内存和日志记录部分,但是弄清楚 CPU 百分比非常困难:( 我发现了很多关于检查 CPU 信息的方法的信息,但在摘要之外几乎没有代码示例我找到了编译,或者评论很好,所以我很难找出一种方法来做到这一点。我已经阅读了很多关于获取 CPU 计时等的 *** 问题,但我没有能够把碎片拼在一起。

也许我没有抓住重点,但解决这个问题的一种流行方法似乎是查询 CPU 两次,每次检查之间至少间隔 200 毫秒,以帮助避免所谓的解决问题?嗯是的!我该怎么做? :( 我在语法上受到挑战 D:

我将分享我的源代码,这样你就可以看到我一直在做什么,直到现在。这一切都在一个 .cpp 中,我使用的是 VS2013 Express for C++,它仅适用于多核 CPU 的 Windows

提前警告:我很抱歉代码中的所有 cmets :x 另外,如果您复制此代码并运行它,它将生成一个名为 log.CSV 的 .CSV 文件

//included libraries/functionality for input/output
#include <iostream>
#include <fstream>
#include <windows.h>
using namespace std;

//creates a static variable to convert Bytes to Megabytes
#define MB 1048576


//main program code loop
int main()

    //Code block intiialization for the memory referenced in the Kernell
    MEMORYSTATUSEX memStat;
    memStat.dwLength = sizeof (memStat);
    GlobalMemoryStatusEx(&memStat);


    //loads the SYSTEMTIME
    SYSTEMTIME sysTime;
    //Retrieves data so that we have a way to Get it to output when using the pointers
    GetSystemTime(&sysTime);


    //setting the I/O for our log file to be "myfile"
    ofstream myfile;
    // ios::out means that we're outputting data to the file
    // ios::app means that all the data we're outputting goes to the end of that log file instead of the start
    myfile.open("log.csv", ios::out | ios::app);


    //a while loop that gathers and logs data every quarter of a second to gather 4 data points in one second
    int counter = 0;
    while (counter < 4)
    
        //Timestamp + Memory Info, and eventually CPU Load percentage
        myfile << sysTime.wHour << ":" << sysTime.wMinute << ":" << sysTime.wMilliseconds << ", " << memStat.dwMemoryLoad << "%, " << memStat.ullTotalPhys / MB << ", " << memStat.ullAvailPhys / MB << ", " << memStat.ullTotalPageFile / MB << ", " << memStat.ullAvailPageFile / MB << ", " << memStat.ullTotalVirtual / MB << ", " << memStat.ullAvailVirtual / MB << ", " << memStat.ullAvailExtendedVirtual / MB << "\n";
        //250 millisecond sleep delay 
        Sleep(250);
        counter = counter + 1;
    
        //close the log file before terminating the program
        myfile.close();

    return 0; //standard main() end of program terminator

编辑#2:

我遇到了这个

BOOL WINAPI GetSystemTimes(_Out_opt_  LPFILETIME lpIdleTime,_Out_opt_  LPFILETIME lpKernelTime,_Out_opt_  LPFILETIME lpUserTime);

它似乎得到了我需要的东西,但我不知道如何实际使用它,甚至不知道如何用它进行单元测试,在将它扔到我的其他 Source 之前,我更愿意这样做。 cpp

我完全迷路了。在过去的几个小时里,我尝试了各种各样的东西,但我什至无法编译一个简单的单元测试。

我觉得这条评论让我走上了正确的道路,但我实际上不知道如何处理它:How is CPU usage calculated?

编辑#3:

我正在展示 Jeremy Friesner 的代码的单元测试,以及我正在开发的完整的日志记录工具。

测试以监控 CPU 负载

#include <Windows.h>
#include <iostream>
using namespace std;

static float CalculateCPULoad();
static unsigned long long FileTimeToInt64();
float GetCPULoad();


int main()
   
    int _c = 0;

    while (_c == 0)
    
        cout << GetCPULoad() * 100 << "\n";
        Sleep(1000);
    

    return 0;



static float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks)

    static unsigned long long _previousTotalTicks = 0;
    static unsigned long long _previousIdleTicks = 0;

    unsigned long long totalTicksSinceLastTime = totalTicks - _previousTotalTicks;
    unsigned long long idleTicksSinceLastTime = idleTicks - _previousIdleTicks;


    float ret = 1.0f - ((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime) / totalTicksSinceLastTime : 0);

    _previousTotalTicks = totalTicks;
    _previousIdleTicks = idleTicks;
    return ret;


static unsigned long long FileTimeToInt64(const FILETIME & ft)

    return (((unsigned long long)(ft.dwHighDateTime)) << 32) | ((unsigned long long)ft.dwLowDateTime);


// Returns 1.0f for "CPU fully pinned", 0.0f for "CPU idle", or somewhere in between
// You'll need to call this at regular intervals, since it measures the load between
// the previous call and the current one.  Returns -1.0 on error.
float GetCPULoad()

    FILETIME idleTime, kernelTime, userTime;
    return GetSystemTimes(&idleTime, &kernelTime, &userTime) ? CalculateCPULoad(FileTimeToInt64(idleTime), FileTimeToInt64(kernelTime) + FileTimeToInt64(userTime)) : -1.0f;

已完成工具(全部进入您的 Source.cpp,然后编译并运行):

/*
Resource Links:
Calling memory info in c++:                             http://msdn.microsoft.com/en-us/library/aa366589%28VS.85%29.aspx
I/O file handling in c++:                               http://www.cplusplus.com/doc/tutorial/files/
Date and Time in c++:                                   http://www.tutorialspoint.com/cplusplus/cpp_date_time.htm
CPU Load Percent (Credit to Jeremy Friesner):           https://***.com/questions/23143693/retrieving-cpu-load-percent-total-in-windows-with-c
Everything else (too many to list):                     https://***.com/
*/


/*
Performance Snapshot Tool

Grabs CPU load percent and basic Memory info from the system,
and or the Windows Task manager

Designed to work with specifically Windows 7 and beyond

Ideology: Create a small executable program to retrieve and
write to a log file a data sample from system performance
in a single snapshot -- robust enough to be called multiple
times per boot

The compiled .exe will be called by another program to run at
an exact, specified time relative to the program that is
calling it

Does 5 checks per second, every 200 milliseconds for a "Snapshot"
of performance

Initial Code Author:    Anonymous
Current Author: Anonymous
Revision:           0.01
Date:               18/4/2014
*/


//included libraries/functionality for input/output
#include <iostream>
#include <fstream>
#include <windows.h>
using namespace std;

//creates a static variable to convert Bytes to Megabytes
#define MB 1048576

//functions to calculate and retrieve CPU Load information
static float CalculateCPULoad();
static unsigned long long FileTimeToInt64();
float GetCPULoad();


//main program code loop
int main()

    //Code block initialization for the memory referenced in the Kernel
    MEMORYSTATUSEX memStat;
    memStat.dwLength = sizeof (memStat);
    GlobalMemoryStatusEx(&memStat);


    //loads the SYSTEMTIME
    SYSTEMTIME sysTime;
    //Retrieves data so that we have a way to Get it to output when using the pointers
    GetSystemTime(&sysTime);


    //setting the I/O for our log file to be "myfile"
    ofstream myfile;
    // ios::out means that we're outputting data to the file
    // ios::app means that all the data we're outputting goes to the end of that log file instead of the start
    myfile.open("log.csv", ios::out | ios::app);


    //a while loop that gathers and logs data every quarter of a second to gather 4 data points in one second
    int counter = 0;
    while (counter < 5)
    
        //Timestamp + Memory Info, and eventually CPU Load percentage
        myfile << sysTime.wHour << "." << sysTime.wMinute << "." << sysTime.wSecond << ", " << GetCPULoad() * 100 << "%, " << memStat.dwMemoryLoad << "%, " << memStat.ullTotalPhys / MB << ", " << memStat.ullAvailPhys / MB << ", " << memStat.ullTotalPageFile / MB << ", " << memStat.ullAvailPageFile / MB << ", " << memStat.ullTotalVirtual / MB << ", " << memStat.ullAvailVirtual / MB << ", " << memStat.ullAvailExtendedVirtual / MB << "\n";
        //250 millisecond sleep delay 
        Sleep(200);
        counter = counter + 1;
    
        //close the log file before terminating the program
        myfile.close();

    return 0; //standard main() end of program terminator


static float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks)

    static unsigned long long _previousTotalTicks = 0;
    static unsigned long long _previousIdleTicks = 0;

    unsigned long long totalTicksSinceLastTime = totalTicks - _previousTotalTicks;
    unsigned long long idleTicksSinceLastTime = idleTicks - _previousIdleTicks;


    float ret = 1.0f - ((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime) / totalTicksSinceLastTime : 0);

    _previousTotalTicks = totalTicks;
    _previousIdleTicks = idleTicks;
    return ret;


static unsigned long long FileTimeToInt64(const FILETIME & ft)

    return (((unsigned long long)(ft.dwHighDateTime)) << 32) | ((unsigned long long)ft.dwLowDateTime);


// Returns 1.0f for "CPU fully pinned", 0.0f for "CPU idle", or somewhere in between
// You'll need to call this at regular intervals, since it measures the load between
// the previous call and the current one.  Returns -1.0 on error.
float GetCPULoad()

    FILETIME idleTime, kernelTime, userTime;
    return GetSystemTimes(&idleTime, &kernelTime, &userTime) ? CalculateCPULoad(FileTimeToInt64(idleTime), FileTimeToInt64(kernelTime) + FileTimeToInt64(userTime)) : -1.0f;

【问题讨论】:

【参考方案1】:

关于 CPU 和内存使用计算的更多详细信息,您可能可以参考下面的链接。

How to determine CPU and memory consumption from inside a process?

此链接包含计算以下点的代码(适用于 Windows 和 Linux)

    可用的总虚拟内存 当前使用的虚拟内存 我的进程当前使用的虚拟内存 可用内存总量 当前使用的内存 我的进程当前使用的 RAM % CPU 当前使用 我的进程当前使用的 CPU 百分比

【讨论】:

【参考方案2】:

最流行的建议解决方案不适用于 Win10 / Visual Studio 2010;使用该方法获得的值似乎与任何事物无关。这可能是因为,正如 Belogorseff 在 cmets 中所指出的,GetSystemTimes 函数返回的内核时间包括空闲时间

有关 GetSystemTimes 函数的说明,请参阅 https://msdn.microsoft.com/en-us/library/windows/desktop/ms724400(v=vs.85).aspx。

此外,我不确定当您将两个无符号数的减法分配给另一个无符号数时会发生什么。似乎这仍然应该是无符号的,但建议的解决方案会对该值小于零进行测试。

我是这样计算“净空”的:

Headroom = time spent in idle
                  / 
        (Kernel time + User time) 

然后“加载”为:

Load = 1 - Headroom

这里是示例代码,您应该可以将其剪切并粘贴到 VS 项目中。如果在 VS 调试器下运行,它将通过 OutputDebugString() 调用将结果显示在调试器的输出窗口中。

// DOSHeadroom.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <Windows.h>
#include <stdio.h>
#include <atlstr.h>
#include <iostream>




__int64 FileTimeToInt64 ( FILETIME & ft )

    ULARGE_INTEGER foo;

    foo.LowPart = ft.dwLowDateTime;
    foo.HighPart = ft.dwHighDateTime;

    return ( foo.QuadPart );



// UI Timer callback

VOID CALLBACK UITimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)

    #define NUMBER_OF_PROCESSORS (8)
    #define PROCESSOR_BUFFER_SIZE (NUMBER_OF_PROCESSORS * 8)
    static ULONG64 ProcessorIdleTimeBuffer [ PROCESSOR_BUFFER_SIZE ];
    CString  ProcessorHeadroomPercentage;

    FILETIME IdleTime, KernelTime, UserTime;
    static unsigned long long PrevTotal = 0;
    static unsigned long long PrevIdle = 0;
    static unsigned long long PrevUser = 0;
    unsigned long long ThisTotal;
    unsigned long long ThisIdle, ThisKernel, ThisUser;
    unsigned long long TotalSinceLast, IdleSinceLast, UserSinceLast;


    // GET THE KERNEL / USER / IDLE times.  
    // And oh, BTW, kernel time includes idle time
    GetSystemTimes( & IdleTime, & KernelTime, & UserTime);

    ThisIdle = FileTimeToInt64(IdleTime);
    ThisKernel = FileTimeToInt64 (KernelTime);
    ThisUser = FileTimeToInt64 (UserTime);

    ThisTotal = ThisKernel + ThisUser;
    TotalSinceLast = ThisTotal - PrevTotal;
    IdleSinceLast = ThisIdle - PrevIdle;
    UserSinceLast = ThisUser - PrevUser;
    double Headroom;
    Headroom =  (double)IdleSinceLast / (double)TotalSinceLast ;
    double Load;
    Load = 1.0 - Headroom;
    Headroom *= 100.0;  // to make it percent
    Load *= 100.0;  // percent

    PrevTotal = ThisTotal;
    PrevIdle = ThisIdle;
    PrevUser = ThisUser;

    // print results to output window of VS when run in Debug
    ProcessorHeadroomPercentage.Format(_T(" Headroom: %2.0lf%%   Load: %2.0lf%%\n"), Headroom, Load);
    OutputDebugString(ProcessorHeadroomPercentage);





void SetupMyTimer (void)

    // set up a timer to periodically update UI, specifically to support headroom display
    // I'll use a timerQueue for this application
    // Create the timer queue.
    HANDLE   hTimerQueue;
    HANDLE   hUITimer;
    enum      UI_TIMER_RATE = 1000 ;  // should happen every 1000 ms or 1Hz.  That should be fast enough

    hTimerQueue = NULL;
    hUITimer = NULL;
    hTimerQueue = CreateTimerQueue();
    CreateTimerQueueTimer( &hUITimer, hTimerQueue, 
         (WAITORTIMERCALLBACK)UITimerRoutine, NULL, 0, UI_TIMER_RATE, 0); //the 1000 means wait 1000ms for first call




int _tmain(int argc, _TCHAR* argv[])

    SetupMyTimer();
    Sleep(10000);
    return 0;

我有一个 TimerQueue 每秒调用一次 UITimerHandler。我认为这是一个可以估计处理器利用率的合理时期。

【讨论】:

顺便说一句,计算值与任务管理器中显示的一致。 您可能希望通过一些补充来改进此答案,以帮助解决整个问题,以及访问它以寻找解决方案的人。这里有一些至少可以帮助我的事情。 1:这对Win10不起作用的信息来源在哪里? 2:TM 的 CPU 值是它做出的估计,因此将其用作指标可能不准确。 3:如果您分享我们可以编译的实际源代码而不是其中的一部分,则可以更轻松地验证答案的有效性,并且更有可能让您的答案获得支持 我的代码中的测试不是小于零,而是大于零。如果函数被立即连续调用两次,它可以避免潜在的被零除问题。 @ElRonaldo fpr me 时间低于任务管理器中显示的时间......我在 Windows 10 中,并尝试了我在 SO 中找到的几种方法(本质上都是一样的) ,它们都显示的时间低于任务管理器......实际上,Process Explorer 显示的时间也与任务管理器不同,虽然 Process Explorer 显示的时间更高......我认为必须有一个必须使用的调整因素,但我不知道那会是什么【参考方案3】:

此代码用于 Cpu 使用情况

FILETIME prevSysIdle, prevSysKernel, prevSysUser;

int getUsage(double &val)

    FILETIME sysIdle, sysKernel, sysUser;
    // sysKernel include IdleTime
    if (GetSystemTimes(&sysIdle, &sysKernel, &sysUser) == 0) // GetSystemTimes func FAILED return value is zero;
        return 0;

    if (prevSysIdle.dwLowDateTime != 0 && prevSysIdle.dwHighDateTime != 0)
    
        ULONGLONG sysIdleDiff, sysKernelDiff, sysUserDiff;
        sysIdleDiff = SubtractTimes(sysIdle, prevSysIdle);
        sysKernelDiff = SubtractTimes(sysKernel, prevSysKernel);
        sysUserDiff = SubtractTimes(sysUser, prevSysUser);

        ULONGLONG sysTotal = sysKernelDiff + sysUserDiff;
        ULONGLONG kernelTotal = sysKernelDiff - sysIdleDiff; // kernelTime - IdleTime = kernelTime, because sysKernel include IdleTime

        if (sysTotal > 0) // sometimes kernelTime > idleTime
            val = (double)(((kernelTotal + sysUserDiff) * 100.0) / sysTotal);
    

    prevSysIdle = sysIdle;
    prevSysKernel = sysKernel;
    prevSysUser = sysUser;

    return 1;



// TIME DIFF FUNC
ULONGLONG SubtractTimes(const FILETIME one, const FILETIME two)

    LARGE_INTEGER a, b;
    a.LowPart = one.dwLowDateTime;
    a.HighPart = one.dwHighDateTime;

    b.LowPart = two.dwLowDateTime;
    b.HighPart = two.dwHighDateTime;

    return a.QuadPart - b.QuadPart;

【讨论】:

您介意提高您的答案吗?不必要;只是一个请求。如果你把你的答案变成一个迷你控制台应用程序,而不仅仅是它的功能,它会让其他人更容易尝试你的答案。这将使其更有可能被使用和投票。 不,答案很好。从什么时候开始这里的答案应该是完整的程序?【参考方案4】:

随着时间的推移计算负载百分比很受欢迎的原因是因为 CPU 并没有真正的可变速度 - 在任何给定的时刻,CPU 内核要么以其额定时钟速率处理指令,要么处于空闲状态,所以瞬时测量只会给您 0% 或 100% (*),这并不是您真正想要的。因此,为了计算有意义的负载百分比,您必须检查 CPU 在特定时间间隔内空闲的时间百分比

无论如何,这里有一些我用来在 Windows 下获取 CPU 使用率值的代码...只需定期调用 GetCPULoad()(例如每 250 毫秒或以您喜欢的任何速率)并乘以 100.0 即可获得百分比:

#include <Windows.h>

static float CalculateCPULoad(unsigned long long idleTicks, unsigned long long totalTicks)

   static unsigned long long _previousTotalTicks = 0;
   static unsigned long long _previousIdleTicks = 0;

   unsigned long long totalTicksSinceLastTime = totalTicks-_previousTotalTicks;
   unsigned long long idleTicksSinceLastTime  = idleTicks-_previousIdleTicks;

   float ret = 1.0f-((totalTicksSinceLastTime > 0) ? ((float)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);

   _previousTotalTicks = totalTicks;
   _previousIdleTicks  = idleTicks;
   return ret;


static unsigned long long FileTimeToInt64(const FILETIME & ft) return (((unsigned long long)(ft.dwHighDateTime))<<32)|((unsigned long long)ft.dwLowDateTime);

// Returns 1.0f for "CPU fully pinned", 0.0f for "CPU idle", or somewhere in between
// You'll need to call this at regular intervals, since it measures the load between
// the previous call and the current one.  Returns -1.0 on error.
float GetCPULoad()

   FILETIME idleTime, kernelTime, userTime;
   return GetSystemTimes(&idleTime, &kernelTime, &userTime) ? CalculateCPULoad(FileTimeToInt64(idleTime), FileTimeToInt64(kernelTime)+FileTimeToInt64(userTime)) : -1.0f;

(*) 好的,您可能会在多核系统上获得更高的分辨率;例如如果您测量四核 CPU 上的瞬时 CPU 使用率,您可能会发现在那个特定时刻,三个核心处于空闲状态,一个核心处于活动状态,并称其为 25% 负载......当然还有英特尔的SpeedStep 实际上改变 CPU 的时钟频率作为管理功耗的一种方式;但我们暂时忽略这些复杂性:)

【讨论】:

哇!那里有一些丝滑的代码,先生 :) 将单元测试与我的任务管理器进行比较显示了相当相似的结果。我喜欢它!!希望没有人不得不徒劳地在网上徘徊,寻找这样一个完美的答案。我将添加一些单元测试和完成的代码,除非其他人想将其用作工具。 @jeremy 有没有办法获取当前进程消耗的CPU百分比? 遗憾的是,它似乎不再适用于带有 Core i5 3317U 的 Windows 10。有什么想法吗? 警告:这里计算 CPU 负载的表达式已经过时了。例如,如果系统空闲和使用的时间相等,即空闲时间 ==(内核时间 + 用户时间),它给出的结果为 0,而不是预期的 0.5(50%)。以此类推。 另外请记住,根据目前的MSDN,内核时间也包括空闲时间!

以上是关于使用 C++ 在 Windows 中检索 CPU 负载百分比总数的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C++ 在 Windows 上检索分页文件大小的“最小允许”/“推荐”/“当前分配”值?

C++ Windows API - 如何检索字体缩放百分比

当 CPU 负载为 100%(主要使用 C++ 和 Qt)时,如何保持 UI 响应?

如何在 C++ 中获取当前的 CPU 和 RAM 使用情况?

c ++检索可执行文件的图标以在Windows注册表图标路径中使用?

如何在 C++ 中获取操作系统名称?