Получение процента загрузки процессора в Windows с помощью С++

Я работаю над этим инструментом, чтобы быстро регистрировать статистику системы, такую ​​как информация о памяти, и процент загрузки процессора (например, то, что показано в диспетчере задач). У меня, похоже, есть память и часть регистрации, о которой идет речь, но выяснение процентного соотношения CPU было очень жестким: (я нашел много информации о методах проверки информации о процессоре, но за пределами абстрактных статей почти ничего из примеров кода Я нашел компиляцию или хорошо прокомментировал, поэтому мне было сложно сделать хэширование этого способа. Я уже прочитал много вопросов о stackoverflow о получении таймингов процессора и тому подобное, но я не был способный объединить куски.

Возможно, мне не хватает смысла, но кажется, что популярный способ понять это - это запросить CPU два раза с по меньшей мере 200 мс между каждой проверкой, чтобы избежать проблем с чем-то, называемым... разрешением? Так что да! Как я это делаю?:( Я синтаксически оспаривается D:

Я собираюсь поделиться своим исходным кодом, чтобы вы могли видеть, что именно я делал до сих пор. Это всего лишь один .cpp, я использую VS2013 Express для С++, и он предназначен только для Windows для многоядерных процессоров.

Предупреждение заранее: мне очень жаль всех комментариев в коде: x Кроме того, если вы скопируете этот код и запустите его, он собирается создать файл .CSV с именем log.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);

Кажется, что он получает материал, который мне нужен, но я не знаю, как его использовать или даже сделать из него unit test, который я бы предпочел, прежде чем выбросить его в остальную часть моего источника .cpp

Я полностью потерян. Я пробовал всевозможные вещи за последние несколько часов, но я даже не могу получить простой компилятор unit test.

Мне кажется, что этот комментарий имеет меня на правильном пути, но я на самом деле не знаю, что с ним делать: Как рассчитано использование процессора?

edit # 3:

Я показываю код unit test для кода Джереми Фризнера, а также завершенный инструмент ведения журнала, над которым я работал.

Тест для контроля загрузки процессора

#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/info/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;
}

Ответ 1

Причина, по которой популярно вычислять процент нагрузки во времени, состоит в том, что CPU не имеют переменной скорости - в любой момент времени ядро ​​процессора является либо инструкцией по обработке с номинальной тактовой частотой, либо сидит без дела, поэтому мгновенное измерение даст вам только 0% или 100% (*), чего вы не хотите. Поэтому, чтобы рассчитать значимый процент нагрузки, вам нужно выяснить, какой процент времени простоя CPU в течение определенного периода времени.

В любом случае, здесь некоторый код, который я использую для получения значения использования ЦП под Windows... просто вызывайте 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;
}

(*) Хорошо, вы можете получить немного большее разрешение в многоядерной системе; например если бы вы измерили мгновенное использование ЦП на четырехъядерном ЦП, вы могли бы обнаружить, что в тот момент времени три ядра были бездействующими, а одно ядро ​​было активным и называет 25% нагрузку... и, конечно, есть такие вещи, как Intel SpeedStep, который фактически изменяет тактовую частоту процессора как способ управления энергопотреблением; но мы будем игнорировать эти осложнения на данный момент:)

Ответ 2

Самое популярное предлагаемое решение для меня не работает на Win10/Visual Studio 2010; значения, полученные с помощью этого метода, по-видимому, не коррелируют ни с чем. Возможно, это связано с тем, что, как отмечается в комментариях Белогорцева, функция 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;
}

У меня UITimerHandler вызывается один раз в секунду с помощью TimerQueue. Я полагал, что это был разумный период, в течение которого можно было бы оценить использование процессора.

Ответ 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;
}