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

Retrieving CPU Load Percent total in Windows with C++

本文关键字:负载 CPU 百分比 中的 Windows C++ 检索      更新时间:2023-10-16

我一直在使用这个工具来快速记录一些系统统计信息,如内存信息和cpu负载百分比(如任务管理器中显示的内容)。我似乎已经处理好了内存和日志部分,但是计算CPU百分比一直非常困难:(我发现了很多关于检查CPU信息的方法的信息,但除了摘要之外,我发现的代码样本几乎没有一个是编译的,或者评论很好,所以我很难找到一种方法来做到这一点

也许我没有抓住重点,但似乎有一种流行的方法可以解决这个问题,那就是在每次检查之间至少200毫秒的时间内查询CPU两次,以帮助避免出现名为…的问题。。。决议所以是的!我到底是怎么做到的(我在语法上受到挑战D:

我将分享我的源代码,这样你就可以看到我到目前为止到底在做什么。这一切都在一个.cpp中,我正在使用VS2013 Express for C++,它只适用于Windows的多核CPU。

提前警告:我对代码中的所有注释感到抱歉: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 的其余部分之前,我更喜欢它

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

我觉得这个评论让我走上了正确的道路,但我真的不知道该怎么办:CPU使用率是如何计算的?

编辑#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://stackoverflow.com/questions/23143693/retrieving-cpu-load-percent-total-in-windows-with-c
Everything else (too many to list):                     https://stackoverflow.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;
}

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

无论如何,以下是我在Windows下获取CPU使用值时使用的一些代码。。。只需定期调用GetCPULoad()(例如,每250mS或以任何您喜欢的速率),然后乘以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的时钟速率,以此来管理功耗;但我们暂时忽略这些复杂问题:)

最流行的建议解决方案在Win10/Visual Studio 2010上不适用;用这种方法获得的值似乎与任何东西都没有关联。也许这是因为,正如Belogortseff在评论中所指出的,内核时间的GetSystemTimes函数返回包括空闲时间

请参阅https://msdn.microsoft.com/en-us/library/windows/desktop/ms724400(v=vs.85).aspx获取GetSystemTimes函数的描述。

此外,我不确定当你把两个无符号数的减法分配给另一个无符号数字时会发生什么。看起来这应该仍然是无符号的,但所提出的解决方案对小于零的值进行了测试。

我这样计算"净空":

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。我认为这是一个可以估计处理器利用率的合理时期。

此代码用于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;
}

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

如何从进程内部确定CPU和内存消耗?

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

  1. 可用虚拟内存总量
  2. 当前使用的虚拟内存
  3. 我的进程当前使用的虚拟内存
  4. 可用的总RAM
  5. 当前使用的RAM
  6. 我的进程当前使用的RAM
  7. %当前使用的CPU
  8. %我的进程当前使用的CPU