web-dev-qa-db-ja.com

C ++を使用するWindowsでCPU負荷率の合計を取得する

私はこのツールに取り組んでいて、メモリ情報やcpu負荷パーセンテージなどのいくつかのシステム統計をすばやくログに記録しています(タスクマネージャに表示されるものなど) 。メモリとロギングの部分は処理されているようですが、CPUの割合を把握するのは非常に困難です。私はコンパイルを見つけた、またはよくコメントされているので、これを行う方法をハッシュ化するのは難しいです。CPUのタイミングなどを取得することに関するスタックオーバーフローの質問をすでに読んだことがあるのですが、ピースを組み合わせることができます。

ポイントを逃しているかもしれませんが、これを理解する一般的な方法は、CPUを2回クエリして、各チェックの間に少なくとも200msを置いて、解決と呼ばれる問題を回避することです。そうそう!一体どうやってそれを行うのですか? :(私は構文的に挑戦されていますD:

ソースコードを共有して、今までのところを正確に把握できるようにします。すべてが1つの.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;
}
16

時間の経過とともに負荷の割合を計算するのが一般的である理由は、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使用率を測定した場合、その特定の瞬間に、3つのコアがアイドルで1つのコアがアクティブであり、その25%の負荷を呼び出すことがわかります...もちろん、Intelのようなものがあります。消費電力を管理する方法として実際にCPUのクロックレートを変化させるSpeedStep。とりあえずこれらの複雑さは無視します:)

16
Jeremy Friesner

最も人気のある提案されたソリューションは、Win10/Visual Studio 2010では機能しません。その方法で得られた値は、何とも相関していないようです。おそらくこれは、Belogortseffのコメントにあるように、のGetSystemTimes関数の戻り値にアイドル時間が含まれるためです

GetSystemTimes関数の説明については、 https://msdn.Microsoft.com/en-us/library/windows/desktop/ms724400(v = vs.85).aspx を参照してください。

さらに、2つの符号なし数値の減算を別の符号なし数値に割り当てるとどうなるかわかりません。それはまだ署名されていないようですが、提案されたソリューションはその値がゼロ未満かどうかのテストを行います。

このようにして「ヘッドルーム」を計算しました。

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によって1秒に1回呼び出されるUITimerHandlerがあります。これは、プロセッサの使用率を推定できる妥当な期間だと思いました。

4
El Ronaldo

このコードは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;
}
1
KimBoGyu