ページ 11

(C++)自プロセスのCPU使用率をプログラムで計算したい

Posted: 2014年7月19日(土) 16:01
by Ketty
こんにちは。Kettyです(^^)

C++で自プロセス(DXライブラリを使用したアプリ)のCPU使用率(%)を求めたく、
PDHカウンタでクエリ発行して取得するというやり方を以下URLを参考に、
CPU使用率をウィンドウ内に表示するように実装してみました。

http://eternalwindows.jp/windevelop/pdh/pdh05.html
http://atelier-peppe.jp/programTips/MFC/MFC_26.html

しかし、タスクマネージャーのプロセスタブのCPU列に表示される値とマッチしません。
マッチさせたいのですが、どこが間違っていますか?
あるいは、考え方自体が誤っておりますか?

OS:Windows 7 64bit Home Edition
Visual Studio2010 Express Edition
※DXライブラリを使用しています

以下、ソースとなります。
およそ0.5秒おきに、自プロセスとtotalのそれぞれのPDHカウンタ値を採取して、CPU使用率を算出しています。
マルチコアにも対応するためにはtotalで割る必要があると以下に記されていたので割り算しています。
http://ameblo.jp/led-sue/entry-10896834648.html

コード:

#include <vector>
#include <pdh.h>
#pragma comment(lib, "pdh.lib")
#include <DxLib.h>

using namespace std ;

// 画面サイズ
static const int SCREEN_W = 640 ;
static const int SCREEN_H = 480 ;

// FPS
static const int FPS = 60 ;

// CPU使用率再計測時間(秒)
static const int CPU_UPDATE_TIME = FPS * 0.5 ;	// だいたい0.5秒ごとに更新してみる

// FPS管理クラス
// Dixqさんのものを移植しただけです dixq.net/rp/43.html
class FpsManager
{
public:
	// コンストラクタ
	FpsManager()
	{
		// 初期化
		vecRecord_.resize( FPS, 0 ) ;
		frameCounter_ = 0 ;
		base0tCounter_ = 0 ;
		//average_ = 0 ;
	}
	// デストラクタ
	~FpsManager(){}

	// FPSを調整する
	void Adjust()
	{
		// この関数内部で使うタイミング変数
		static int timing_ = 0 ;

		// 待つべき時間を求める
		int waitMillisecond = 0 ;
		// 60フレーム中の1フレーム目のとき
		if( frameCounter_ == 0 )
		{
			// 完全に最初なら
			if( timing_ == 0 )
			{
				// 待つべき時間=なし
				waitMillisecond = 0 ;
			}
		
			else
			{
				// 待つべき時間=前回記録した時間を元に計算
				waitMillisecond = base0tCounter_ + 1000 - DxLib::GetNowCount() ;
			}
		}
		// 上記以外のとき
		else
		{
			// 待つべき時間=現在あるべき時刻-現在の時刻
			waitMillisecond = static_cast<int>( base0tCounter_ + frameCounter_ * ( 1000.0 / FPS ) ) - DxLib::GetNowCount() ;
		}

		// 待つべき時間だけ待つ
		if( waitMillisecond > 0 )
		{
			Sleep( waitMillisecond ) ;
		}

		// 現在時間を取得
		const int gnt = DxLib::GetNowCount() ;

		// 60フレームに1度基準を作る
		if( frameCounter_ == 0 )
		{
			base0tCounter_ = gnt ;
		}

		//1周した時間を記録
		vecRecord_[frameCounter_] = gnt - timing_ ;
		// タイミング更新
		timing_ = gnt ;

		// 60フレーム中の最終フレームのときのみ平均計算
		if( frameCounter_ == FPS - 1 )
		{
			average_ = 0;
			for( int i=0; i<FPS; ++i )
			{
				average_ += vecRecord_[i] ;
			}
			average_ /= FPS ;
		}

		// フレームカウンタ更新
		frameCounter_ = ( ++frameCounter_ ) % FPS ;
	}

	// FPS測定値を取得する
	double GetFps()
	{
		if( average_ > 0 )
		{
			return 1000.0 / average_ ;
		}
		else
		{
			return 0.0 ;
		}
	}
private :
	// 平均を計算するための記録用変数(60回の1周時間を記録)
	vector<int> vecRecord_ ;
	// フレームのカウンタ
	int frameCounter_ ;
	// 60フレームに1回基準となる時刻を記録する変数
	int base0tCounter_ ;
	// 平均fps
	double average_ ;
} ;

// CPU使用率管理クラス
class CpuManager
{
public:
	// コンストラクタ
	CpuManager() : isInitSuccess_( false )
	{
		cpuUseRate_ = 0.0 ;

		//--------------------------------------
		// 自プロセスのクエリデータを生成
		//--------------------------------------
		queryDataOwn_ = new QUERY_DATA ;
		// 新規クエリを作成
		PdhOpenQuery( NULL, 0, &queryDataOwn_->hQuery ) ;
		PdhAddCounter( queryDataOwn_->hQuery, TEXT( "\\Process(CpuUseRateTest)\\% Processor Time" ), 0, &queryDataOwn_->hCounter ) ;
		// この時点での値を取得
		 PdhGetRawCounterValue( queryDataOwn_->hCounter, NULL, &queryDataOwn_->rawValueStack ) ;
		//--------------------------------------
		// Totalのクエリデータを生成
		//--------------------------------------
		queryDataTotal_ = new QUERY_DATA ;
		// 新規クエリを作成
		PdhOpenQuery( NULL, 0, &queryDataTotal_->hQuery ) ;
		PdhAddCounter( queryDataTotal_->hQuery, TEXT( "\\Process(_Total)\\% Processor Time" ), 0, &queryDataTotal_->hCounter ) ;
		// この時点での値を取得
		PdhGetRawCounterValue( queryDataTotal_->hCounter, NULL, &queryDataTotal_->rawValueStack ) ;

		// 初期化成功
		isInitSuccess_ = true ;
	}
	// デストラクタ
	~CpuManager()
	{
		// 後始末
		PdhCloseQuery( queryDataOwn_ ) ;
		PdhCloseQuery( queryDataTotal_ ) ;
		delete queryDataOwn_ ;
		delete queryDataTotal_ ;
	}

	// 更新する
	void Update()
	{
		// 初期化失敗していれば処理しない
		if( isInitSuccess_ == false ){ return ; }

		// 現在値
		PDH_RAW_COUNTER rawValue ;

		//--------------------------------------
		// 自プロセスのデータを更新
		//--------------------------------------
		// データ収集
		PdhCollectQueryData( queryDataOwn_->hQuery ) ;
		// この時点での値を取得
		PdhGetRawCounterValue( queryDataOwn_->hCounter, NULL, &rawValue ) ;
		// この時点での値と前回の値から計測して結果をdouble型にフォーマットする
		PdhCalculateCounterFromRawValue( queryDataOwn_->hCounter, PDH_FMT_DOUBLE, &rawValue, &queryDataOwn_->rawValueStack, &queryDataOwn_->fmtValue ) ;
		// この時点での値を保持
		queryDataOwn_->rawValueStack = rawValue ;

		//--------------------------------------
		// Totalのデータを更新
		//--------------------------------------
		// データ収集
		PdhCollectQueryData( queryDataTotal_->hQuery ) ;
		// この時点での値を取得
		PdhGetRawCounterValue( queryDataTotal_->hCounter, NULL, &rawValue ) ;
		// この時点での値と前回の値から計測して結果をdouble型にフォーマットする
		PdhCalculateCounterFromRawValue( queryDataTotal_->hCounter, PDH_FMT_DOUBLE, &rawValue, &queryDataTotal_->rawValueStack, &queryDataTotal_->fmtValue ) ;
		// この時点での値を保持
		queryDataTotal_->rawValueStack = rawValue ;

		// CPU使用率をパーセントで設定する
		const double own = queryDataOwn_->fmtValue.doubleValue ;
		const double total = queryDataTotal_->fmtValue.doubleValue ;
		cpuUseRate_ = ( own / total ) * 100 ;	// 自プロセスの使用率÷Totalの使用率
	}

	// CPU使用率をパーセントで取得する
	double GetCpuUseRate()
	{
		return cpuUseRate_ ;
	}
private :
	// PDHクエリ関連をまとめるための構造体
	struct QUERY_DATA
	{
		PDH_HQUERY				hQuery ;		// クエリ
		PDH_HCOUNTER			hCounter ;		// クエリで取得する項目を表すオブジェクト
		PDH_RAW_COUNTER			rawValueStack ;	// 現在値(最新値)を保持する変数
		PDH_FMT_COUNTERVALUE	fmtValue ;		// 現在値と前回値とを比較して得られる値
	} ;

	// PDHクエリデータ
	QUERY_DATA *queryDataOwn_ ;		// 自プロセスのProcessor Time(CPU使用率)
	QUERY_DATA *queryDataTotal_ ;	// TotalのProcessor Time(CPU使用率)
	// CPU使用率(%)
	double cpuUseRate_ ;
	// 初期化成功フラグ
	bool isInitSuccess_ ;
};

// Main関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	// DXライブラリのログ出力をしない
	DxLib::SetOutApplicationLogValidFlag( FALSE ) ;
	// ウィンドウモードで起動
	DxLib::ChangeWindowMode( TRUE ) ;
	// 画面モード設定
	DxLib::SetGraphMode( SCREEN_W, SCREEN_H, 32 ) ;
	// ウィンドウを最小化しても常に動くようにする
	DxLib::SetAlwaysRunFlag( TRUE ) ;

	// DXライブラリ初期化処理
	if( DxLib::DxLib_Init() == -1 ){ return -1 ; }
	// 画像を読み込み
	int graphHandle = DxLib::LoadGraph( "Test1.png" ) ;
	// フォント設定
	int fontHandle = DxLib::CreateFontToHandle( "メイリオ", 20, DX_FONTTYPE_ANTIALIASING ) ;
	const int fontColor = DxLib::GetColor( 255, 255, 255 ) ;

	// FPS管理クラスのインスタンス生成
	FpsManager *fpsManager = new FpsManager() ;
	// CPU使用率管理クラスのインスタンス生成
	CpuManager *cpuManager = new CpuManager() ;

	// CPU使用率用の更新カウンター
	int cnt = 0 ;

	// メインループ(ESCで終了)
	while( DxLib::ProcessMessage() == 0 && DxLib::CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
	{
		// 画面クリア
		DxLib::ClsDrawScreen();

		// CPU使用率再算出用カウンター更新
		++cnt ;
		// 既定時間ごとにCPU使用率を更新する
		if( cnt == CPU_UPDATE_TIME )
		{
			cpuManager->Update() ;
			cnt = 0 ;
		}

		// 画像を描画
		DxLib::DrawGraph( 100, 100, graphHandle, TRUE ) ;
		
		// CPU使用率表示
		DxLib::DrawFormatStringToHandle( 0, 0, fontColor, fontHandle, "[%.1f]", cpuManager->GetCpuUseRate() ) ;

		// FPS表示
		DxLib::DrawFormatStringToHandle( 0, SCREEN_H - 20, fontColor, fontHandle, "[%.1f]", fpsManager->GetFps() ) ;

		// FPS調整
		fpsManager->Adjust();
	}

	// クラスのインスタンス破棄
	delete fpsManager ;
	delete cpuManager ;

	// DXライブラリ終了処理
	DxLib::DxLib_End() ;

	// ソフトの終了 
	return 0 ;
}
これを実行しますと、私の環境では
タスクマネージャーのプロセスタブのCPU列では00~01が表示されますが、
アプリのウィンドウ内には3.1~9.5などという値が表示されてしまいます。

よろしくお願いします。

Re: (C++)自プロセスのCPU使用率をプログラムで計算したい

Posted: 2014年7月26日(土) 14:13
by Ketty
自己解決できました。

以下が誤りでした。
× PDHカウンタで取得した自プロセスのProcessor Timeを、Totalで割る
○ PDHカウンタで取得した自プロセスのProcessor Timeを、CPUコア数で割る

参考までにコードを残しておきます

コード:

#include <vector>
#include <pdh.h>				// PDH関連
#pragma comment(lib, "pdh.lib")
#include <DxLib.h>

using namespace std ;

// 画面サイズ
static const int SCREEN_W = 640 ;
static const int SCREEN_H = 480 ;

// FPS
static const int FPS = 60 ;

// CPU使用率再計測時間(秒)
static const int CPU_UPDATE_TIME = FPS / 2 ;	// だいたい0.5秒ごとに更新

// FPS管理クラス
// Dixqさんのものを移植しただけです http://dixq.net/rp/43.html
class FpsManager
{
public:
	// コンストラクタ
	FpsManager()
	{
		// 初期化
		vecRecord_.resize( FPS, 0 ) ;
		frameCounter_ = 0 ;
		base0tCounter_ = 0 ;
		//average_ = 0 ;
	}
	// デストラクタ
	~FpsManager(){}

	// FPSを調整する
	void Adjust()
	{
		// この関数内部で使うタイミング変数
		static int timing_ = 0 ;

		// 待つべき時間を求める
		int waitMillisecond = 0 ;
		// 60フレーム中の1フレーム目のとき
		if( frameCounter_ == 0 )
		{
			// 完全に最初なら
			if( timing_ == 0 )
			{
				// 待つべき時間=なし
				waitMillisecond = 0 ;
			}
		
			else
			{
				// 待つべき時間=前回記録した時間を元に計算
				waitMillisecond = base0tCounter_ + 1000 - DxLib::GetNowCount() ;
			}
		}
		// 上記以外のとき
		else
		{
			// 待つべき時間=現在あるべき時刻-現在の時刻
			waitMillisecond = static_cast<int>( base0tCounter_ + frameCounter_ * ( 1000.0 / FPS ) ) - DxLib::GetNowCount() ;
		}

		// 待つべき時間だけ待つ
		if( waitMillisecond > 0 )
		{
			Sleep( waitMillisecond ) ;
		}

		// 現在時間を取得
		const int gnt = DxLib::GetNowCount() ;

		// 60フレームに1度基準を作る
		if( frameCounter_ == 0 )
		{
			base0tCounter_ = gnt ;
		}

		//1周した時間を記録
		vecRecord_[frameCounter_] = gnt - timing_ ;
		// タイミング更新
		timing_ = gnt ;

		// 60フレーム中の最終フレームのときのみ平均計算
		if( frameCounter_ == FPS - 1 )
		{
			average_ = 0;
			for( int i=0; i<FPS; ++i )
			{
				average_ += vecRecord_[i] ;
			}
			average_ /= FPS ;
		}

		// フレームカウンタ更新
		frameCounter_ = ( ++frameCounter_ ) % FPS ;
	}

	// FPS測定値を取得する
	double GetFps()
	{
		if( average_ > 0 )
		{
			return 1000.0 / average_ ;
		}
		else
		{
			return 0.0 ;
		}
	}
private :
	// 平均を計算するための記録用変数(60回の1周時間を記録)
	vector<int> vecRecord_ ;
	// フレームのカウンタ
	int frameCounter_ ;
	// 60フレームに1回基準となる時刻を記録する変数
	int base0tCounter_ ;
	// 平均fps
	double average_ ;
} ;

// CPU使用率管理クラス
class CpuManager
{
public:
	// コンストラクタ
	CpuManager() : isInitSuccess_( false )
	{
		cpuUseRate_ = 0.0 ;

		//--------------------------------------
		// CPUコア数を取得する
		//--------------------------------------
		SYSTEM_INFO sysInfo ;
		GetSystemInfo( &sysInfo ) ;
		cpuNum_ = sysInfo.dwNumberOfProcessors ;

		//--------------------------------------
		// 自プロセスのクエリデータを生成
		//--------------------------------------
		queryData_ = new QUERY_DATA ;
		// 新規クエリを作成
		PdhOpenQuery( NULL, 0, &queryData_->hQuery ) ;
		PdhAddCounter( queryData_->hQuery, TEXT( "\\Process(CpuUseRateTest)\\% Processor Time" ), 0, &queryData_->hCounter ) ;
		// この時点での値を取得
		 PdhGetRawCounterValue( queryData_->hCounter, NULL, &queryData_->rawValueStack ) ;

		// 初期化成功
		isInitSuccess_ = true ;
	}
	// デストラクタ
	~CpuManager()
	{
		// クエリ終了
		PdhCloseQuery( queryData_ ) ;
		delete queryData_ ;
	}

	// 更新する
	void Update()
	{
		// 初期化失敗していれば処理しない
		if( isInitSuccess_ == false ){ return ; }

		// 現在値
		PDH_RAW_COUNTER rawValue ;

		//--------------------------------------
		// 自プロセスのデータを更新
		//--------------------------------------
		// データ収集
		PdhCollectQueryData( queryData_->hQuery ) ;
		// この時点での値を取得
		PdhGetRawCounterValue( queryData_->hCounter, NULL, &rawValue ) ;
		// この時点での値と前回の値から計測して結果をdouble型にフォーマットする
		PdhCalculateCounterFromRawValue( queryData_->hCounter, PDH_FMT_DOUBLE, &rawValue, &queryData_->rawValueStack, &queryData_->fmtValue ) ;
		// この時点での値を保持
		queryData_->rawValueStack = rawValue ;

		// CPU使用率をパーセントで設定する
		const double ownCpuUseRate = queryData_->fmtValue.doubleValue ;
		cpuUseRate_ = ownCpuUseRate / cpuNum_ ;	// 自プロセスの使用率÷CPUコア数
	}

	// CPU使用率をパーセントで取得する
	double GetCpuUseRate()
	{
		return cpuUseRate_ ;
	}
private :
	// PDHクエリ関連をまとめるための構造体
	struct QUERY_DATA
	{
		PDH_HQUERY				hQuery ;		// クエリ
		PDH_HCOUNTER			hCounter ;		// クエリで取得する項目を表すオブジェクト(カウンターという)
		PDH_RAW_COUNTER			rawValueStack ;	// 現在値(最新値)を保持する変数
		PDH_FMT_COUNTERVALUE	fmtValue ;		// 現在値と前回値とを比較して得られる値(求めたいのはこれ)
	} ;

	// PDHクエリデータ
	QUERY_DATA *queryData_ ;
	// CPU使用率(%)
	double cpuUseRate_ ;
	// CPUコア数
	int cpuNum_ ;
	// 初期化成功フラグ
	bool isInitSuccess_ ;
};

// Main関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
	// DXライブラリのログ出力をしない
	DxLib::SetOutApplicationLogValidFlag( FALSE ) ;
	// ウィンドウモードで起動
	DxLib::ChangeWindowMode( TRUE ) ;
	// 画面モード設定
	DxLib::SetGraphMode( SCREEN_W, SCREEN_H, 32 ) ;
	// ウィンドウを最小化しても常に動くようにする
	DxLib::SetAlwaysRunFlag( TRUE ) ;

	// DXライブラリ初期化処理
	if( DxLib::DxLib_Init() == -1 ){ return -1 ; }
	// 画像を読み込み
	int graphHandle = DxLib::LoadGraph( "Test1.png" ) ;
	// フォント設定
	int fontHandle = DxLib::CreateFontToHandle( "メイリオ", 20, DX_FONTTYPE_ANTIALIASING ) ;
	const int fontColor = DxLib::GetColor( 255, 255, 255 ) ;

	// FPS管理クラスのインスタンス生成
	FpsManager *fpsManager = new FpsManager() ;
	// CPU使用率管理クラスのインスタンス生成
	CpuManager *cpuManager = new CpuManager() ;

	// CPU使用率用の更新カウンター
	int cnt = 0 ;

	// メインループ(ESCで終了)
	while( DxLib::ProcessMessage() == 0 && DxLib::CheckHitKey( KEY_INPUT_ESCAPE ) == 0 )
	{
		// 画面クリア
		DxLib::ClsDrawScreen();

		// CPU使用率再算出用カウンター更新
		++cnt ;
		// 既定時間ごとにCPU使用率を更新する
		if( cnt == CPU_UPDATE_TIME )
		{
			cpuManager->Update() ;
			cnt = 0 ;
		}

		// 画像を描画
		DxLib::DrawGraph( 100, 100, graphHandle, TRUE ) ;
		
		// CPU使用率表示
		DxLib::DrawFormatStringToHandle( 0, 0, fontColor, fontHandle, "[%.1f]", cpuManager->GetCpuUseRate() ) ;

		// FPS表示
		DxLib::DrawFormatStringToHandle( 0, SCREEN_H - 20, fontColor, fontHandle, "[%.1f]", fpsManager->GetFps() ) ;

		// FPS調整
		fpsManager->Adjust();
	}

	// クラスのインスタンス破棄
	delete fpsManager ;
	delete cpuManager ;

	// DXライブラリ終了処理
	DxLib::DxLib_End() ;

	// ソフトの終了 
	return 0 ;
}