CPU使用率を下げるには?FPSを安定させるには?

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
よっつん

CPU使用率を下げるには?FPSを安定させるには?

#1

投稿記事 by よっつん » 18年前

始めまして、よっつんと申します。

FPSを管理するプログラムを作っているのですが、
FlipScreen()後に、以下のような処理をしてFPSを60FPSにしようとしています。
動作はまぁまぁ期待した通りに動いているのですが、2つ問題がありまして、どうしても解決できません。

一つ目は、CPUの使用率が異様に高いことです。
私のPCでは、約13ミリ秒以上SleepとかWaitTimerやwhileなどで処理を休ませると、
CPU使用率が10%以下から一気に45%程度まで上昇してしまいます。
WaitTimer()の処理を辞めたり、12ミリ秒以下の処理停止では、10%以下になります。
何が原因かさっぱり分かりません。

2つ目は、このプログラムだと、Fpsが56-60までかなり広範囲にぶれてしまうことです。
描画処理などは、一枚絵を表示して、FPSとWaitTimeを表示するだけのものです。
処理落ちするような計算はしていないと思うのですが・・・

筆不精で申し訳ありませんが、よろしくお願いします。
if( NowTime - OldFrameTime <= 16667 ){//処理スピードが60FPSを超えているので補正する。

		WaitTime =  OldFrameTime + 16667 - NowTime ;
		//余った時間。
				
		//while( GetNowHiPerformanceCount() - OldFrameTime <= 16667 ) {Sleep(1);}
		//Sleep( 13 );
		WaitTimer( ( int ) ( WaitTime / 1000 ) );//規定時間まで待つ。
		//マイクロ秒からミリ秒に直しつつintにキャスト。

		OldFrameTime = OldFrameTime + 16667 ;
	
	}
	else OldFrameTime = NowTime ;
使用PC
Windows2000
Pentiumⅲ866Mhz
モニタ75Mhz

管理人

Re:CPU使用率を下げるには?FPSを安定させるには?

#2

投稿記事 by 管理人 » 18年前

ゲームプログラミングの館の「FPSを表示する」のサンプルプログラムを実行するとどうなりますか?
http://dixq.net/g/#s6

tk-xleader

Re:CPU使用率を下げるには?FPSを安定させるには?

#3

投稿記事 by tk-xleader » 18年前

GetNowHiPerformanceCount()の処理が重いのでは?

よっつん

Re:CPU使用率を下げるには?FPSを安定させるには?

#4

投稿記事 by よっつん » 18年前

早速返信ありがとうございます。

サンプルプログラムを実行しましたが、同じ結果になりましたw(:_;)w

一つ一つの処理を止めて、CPU使用率を見たところ、
GetNowHiPerfomanceCount()の処理は殆ど影響がありませんでした。

WaitTime()やSleep()などを実行したときのみ処理が異様に重くなります。

取りあえず、強制リフレッシュレート60ヘルツ化の荒業を使おうと思います。

FPSのばらつきは、モニタが75ヘルツなので、誤差がでるのは当然でした(;´Д`)

Justy

Re:CPU使用率を下げるには?FPSを安定させるには?

#5

投稿記事 by Justy » 18年前

 再現できるソースコードがあれば、テストが出来るんですが・・・。
 その一部分だけじゃどうにも。

 でも適当に推測を。
 SetWaitVSyncFlag()は設定していますか?
 していないのであれば垂直同期待ちをしないように設定してみて下さい。

管理人

Re:CPU使用率を下げるには?FPSを安定させるには?

#6

投稿記事 by 管理人 » 18年前

私も、while文で待機させると、少々低いスペックのPCだとCPU稼働率が上がった事があります。
ただ、while文を使わないのでしたらGetNowHiPerfomanceCount()を使う意味があるのでしょうか?
どのみちSleep関数などで待たせる事の出来る時間はミリ秒なので、取得する時間もミリ秒単位である
GetNowCount()をお使いになってみてはいかがでしょうか。

もしwhileでループさせながら待機させているならCPU稼働率が異様に上がってしまうのはわかりますが、
そうでないんですよね。
CPUを常に動かしながら待機させるより止めた方がCPUの為によさそうです。
なお、DXライブラリを使うのでしたら、Sleep関数よりメッセージ処理をしてくれる
WaitTimerの方が良いようです。

Justy

Re:CPU使用率を下げるには?FPSを安定させるには?

#7

投稿記事 by Justy » 18年前

 あー、ちなみに件の「FPSを表示する」のサンプルプログラムは
うちの環境でそのまま動かすと 30~40%くらいでしたが
SetWaitVSyncFlag()を使ったら、0%になりました(w

よっつん

Re:CPU使用率を下げるには?FPSを安定させるには?

#8

投稿記事 by よっつん » 18年前

>Dixq様
>Justy様
素早い詳しい回答ありがとうございます。

SetWaitVSyncFlag()使ってみました。CPU使用率が一気に下がりましたヾ(´▽`;)ゝ
ついでに、FPSの精度も60+-0.15まで上がりました。
詳しい仕組みはよく分かりませんが、
WaitTime()等でCPU使用率を下げるには、VSYNC同期を取らないように設定すればいいのですね。

ただ環境によっては垂直同期を取らないとちらつく場合があるようなので、(私のPCではごく僅かにちらつきました)
基本は垂直同期無視で
オプションでフルスクリーンの場合は強制60ヘルツに出来るようにしようと思います。

GetNowHiPerfomanceCount()を使っているのは、FPSの精度を上げるためです。
処理落ちしていないかぎり、大体おおよそ60FPSくらいになるように処理を止めています。
ここで出た誤差は、LONGLONG OldFrameTimeで補正しています。(OldFrameTimeはほぼきっちり正確に60FPSで推移していくので)

Justy

Re:CPU使用率を下げるには?FPSを安定させるには?

#9

投稿記事 by Justy » 18年前

 無事解決して何よりです。

> 詳しい仕組みはよく分かりませんが
 多分、内部的に ScreenFlip()のタイミングで、垂直同期開始待ちが発生し、
(75Mhzなら 13.3333ms間隔になるよう)強制的にウエイトが入るのでしょう。

 その処理が WaitTime()のような Sleep()を用いたものなら良かったのですが、
多分スレッドを止めることなく待っている為、その分 CPUを消費しているものと思われます。


> 垂直同期を取らないとちらつく
 これはしょうがないですね。

よっつん

Re:CPU使用率を下げるには?FPSを安定させるには?

#10

投稿記事 by よっつん » 18年前

おかげさまで解決できました。ありがとうございました。
問題のプログラムはこんなのでした。SetWaitVSyncFlag()の行のコメントアウトすると
処理が重たくなります。
test.pngを表示して、上下左右に動かすプログラムです。ESCで終了です。

ところで、fpsという変数で割り算するのですが、fpsが0の時の例外処理も書くべきでしょうか?
ほとんど天文学的確率かなと思うのですが。
#include "DxLib.h"

class FpsClass{

	private:
		
		//定数の宣言と初期化。他に定義が必ず必要。
		static const int FpsRenewalCounter ;
		
		//変数の宣言。
		//int x = 0 ;
		LONGLONG 	OldFpsCountTime ;//FPS計測のカウントの内最初の描画成功時の時間
		LONGLONG 	OldFrameTime ;	//前フレームの終了時間。16ミリ秒を超えたかどうかの判定に使う。
		LONGLONG 	fps ;		//FPSを記憶。
		LONGLONG 	WaitTime ;	//処理が早すぎるときに待つ時間。
		int	FlipCount ;	//FpsRenewalCounter回フリップされるごとにFPSを更新。
	
		
	public://子クラスからアクセスするバージョンではプロテクトにする。
		FpsClass();//コンストラクタ
		int GetFps();//LONGLONG fpsをintにキャストして返す。
		int GetWaitTime();//↑のWaitTime版。
		void FpsControl();
		//↑全ての描画後に呼び出す。描画回数をカウントして、30回描画するごとにFPSを計算する。
		//また、60FPSを保つ役割も持つ。

};

const int FpsClass::FpsRenewalCounter = 15 ;//FPSを何フレーム毎に更新するか

FpsClass::FpsClass(){

	fps = 0 ;
	FlipCount = 0 ;
	WaitTime= 0 ;
	OldFpsCountTime = GetNowHiPerformanceCount() ;//計測開始
	OldFrameTime = OldFpsCountTime ;

}

int FpsClass::GetFps(){

	return ( int ) fps ;

}

int FpsClass::GetWaitTime(){

	return ( int ) WaitTime ;

}

void FpsClass::FpsControl(){

	LONGLONG NowTime ;//時間を記憶。
	//int IntWaitTime ;//悪者探しに使いました。>解決済み。
	
	FlipCount++;

	NowTime = GetNowHiPerformanceCount();

	if( FlipCount == FpsRenewalCounter ){

		FlipCount = 0 ;
			
		fps = NowTime - OldFpsCountTime ;
			
		fps = 100000000 / fps * FpsRenewalCounter ;
		//実際は1秒は1000000だが、あえて100倍してある。
		//これは、FPS描画用関数で小数点第二位まで表示するために便利だから・・・けど修正予定

		OldFpsCountTime = NowTime ;

	}



	if( NowTime - OldFrameTime <= 16667 ){//処理スピードが60FPSを超えているので補正する。

		WaitTime =  OldFrameTime + 16667 - NowTime ;
		//余った時間。
		
		//以下は悪役探しに使いました。>CPU使用率が上がる問題
		//IntWaitTime = ( int ) ( WaitTime / 1000 );//どこが悪いの???
		//WaitTimer( IntWaitTime );
                  //while( GetNowHiPerformanceCount() - OldFrameTime <= 16667 ) {Sleep( 1 )}
		//Sleep( 13 );
		
		WaitTimer( ( int ) ( WaitTime / 1000 ) );//規定時間まで待つ。
		//ミリ秒に直しつつintにキャスト。

		OldFrameTime = OldFrameTime + 16667 ;//WaitTimerの誤差があるはずだが、(・ε・)キニシナイ!!
	
	}
	else{
	
		OldFrameTime = NowTime ;
	
		WaitTime = 0 ;
	
	}

}

int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
				 LPSTR lpCmdLine, int nCmdShow )
{

	SetWindowText("表示のテスト");
	
	SetOutApplicationLogValidFlag(FALSE);
	
	ChangeWindowMode(TRUE);//デバッグモード専用
	
	// DXライブラリ初期化処理
	if( DxLib_Init() == -1 ) return -1 ;

	//垂直同期をとらない
	SetWaitVSyncFlag( FALSE ) ;

	// 描画先を裏画面にする
	SetDrawScreen( DX_SCREEN_BACK ) ;
////////////////////////////////////////////////////////以上は前処理

	int x = 0 ;
	int y = 0 ;
	int MyCount = 0 ;
	int MyWaitTime = 0 ;
	double fpsnum  = 0 ;

	int GrHandle ;
	int Color ;
	GrHandle = LoadGraph( "test.png" ) ;
	Color = GetColor( 255 , 255 , 255 ) ;

	FpsClass myfps ;
	
	while( 1 )
	{
		// 画面に描かれているものをすべて消す
		ClearDrawScreen() ;

		// 上下左右のキー入力に対応して x, y の座標値を変更する
		if( CheckHitKey( KEY_INPUT_LEFT ) == 1 ) x -= 8 ;
		if( CheckHitKey( KEY_INPUT_RIGHT ) == 1 ) x += 8 ;
		if( CheckHitKey( KEY_INPUT_UP ) == 1 ) y -= 8 ;
		if( CheckHitKey( KEY_INPUT_DOWN ) == 1 ) y += 8 ;

		// x , y が示す画面座標に画像を描画する
		DrawGraph( x , y , GrHandle , TRUE ) ;
		
		MyCount++ ;
		
		if( MyCount == 30 ){//処理にどのくらい余裕があるかを見る。
			MyCount = 0 ;
			MyWaitTime = myfps.GetWaitTime();
		}

		DrawFormatString( 0, 0, Color, "FPS %d ", myfps.GetFps() ) ;
		DrawFormatString( 0, 200, Color, "Wait %d ", MyWaitTime ) ;

		// 裏画面の内容を表画面に反映させる
		ScreenFlip() ;
		
		//描画後に必ず呼ぶ。
		myfps.FpsControl() ;

		// Windows システムからくる情報を処理する
		if( ProcessMessage() == -1 ) break ;

		// ESCキーが押されたらループから抜ける
		if( CheckHitKey( KEY_INPUT_ESCAPE ) == 1 ) break ;
	}



//////////////////////////////////////////////////////以下は終了処理
	// DXライブラリ使用の終了処理
	DxLib_End() ;

	// ソフトの終了
	return 0 ;

}
<!--3-->

よっつん

Re:CPU使用率を下げるには?FPSを安定させるには?

#11

投稿記事 by よっつん » 18年前

連投すみません。入れ違いになってしまいました。

>Justy様
なるほど納得しました。WaitTimerでCPU使用率が高くなっているのではなく
もともと高かったものが、多少下がったというわけですね。
ありがとうございました\(*^▽^*)/

Justy

Re:CPU使用率を下げるには?FPSを安定させるには?

#12

投稿記事 by Justy » 18年前

> 連投すみません。入れ違いになってしまいました
 いえいえ、お気になさらず。


> fpsが0の時の例外処理も書くべきでしょうか
 0は 100%ありえない、というケースを除いてしておいた方がいいでしょう。
 万が一 0除算が発生してアプリが落ちた、なんて滅多に起きなければ起きないほど、
バグがとれない謎の現象になるのでそういうのは事前に潰しておかないと・・・。


> WaitTimerでCPU使用率が高くなっているのではなく
 そんな感じだと思います。
 ただ、WaitTimer自身は内部で ProcessMessage()が呼ばれるので、
メッセージの処理に負荷がかかるとここが重くなる可能性はあります。

tk-xleader

Re:CPU使用率を下げるには?FPSを安定させるには?

#13

投稿記事 by tk-xleader » 18年前

ひとつ気になった点があるのですが、実行時にメモリが足りてないので仮想メモリか何かで処理が重くなったとかという可能性はありませんか?

よっつん

Re:CPU使用率を下げるには?FPSを安定させるには?

#14

投稿記事 by よっつん » 18年前

やはり念には念をいれるべきですよね。わかる範囲でエラー処理をしていこうとおもいます。

仮想メモリで処理が重くなる可能性もあるかもしれませんが、
今回のは違うようです。ScreenFlip()がCPUを占有している可能性が高いと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る