【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
ホヅミ
記事: 110
登録日時: 9年前

【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#1

投稿記事 by ホヅミ » 7年前

現在私はC++CLI(今更感が否めない)のBackgroundWorkerとOpenCVを用いて、
カメラから得た画像データをバックグランドで連続保存を行っています。
前回の質問ではi7 2700Kを使っており、難なく保存ができておりました。
しかし、下記にあるCPUを使って連続撮影を行った場合、最初は調子が良かったのですが、保存を進めていくうちに動作が重くなりプログラムが止まってしまいます。

質問を箇条書きにすると
・そもそもBackgroundWorkerはマルチスレッドなのか?
・原因はHDDのアクセスの問題か?CPUに負荷をかけ過ぎなのか?
・動作が重くなるのはただ単にスペックが足りていないのか?
・どこを改善すればよいのか?

です。
C++/CLIは必ず使うものとします。
事実、私は、マルチスレッドもクロック周波数もアクセス速度がどうのこうのも右も左もわからない状態です。
以上のことよろしくお願いいたします。

OS:Win7 CPU:i7 L640(Core i7-640LM?)
コンパイラ:VS2010
ライブラリ:OpenCV2.4

以下、ソースコード
OpenCVのヘッダとライブラリ読み込み部分省略
iDSProgamForm.h

コード:

#pragma once

namespace iDSProgram {

	using namespace System;
	using namespace System::Text;
	using namespace System::ComponentModel;
	using namespace System::Collections;
	using namespace System::Collections::Generic;
	using namespace System::Windows::Forms;
	using namespace System::Data;
	using namespace System::Drawing;
	
	/// <summary>
	/// iDSProgramForm の概要
	/// </summary>
	public ref class iDSProgramForm : public System::Windows::Forms::Form
	{
	public:
		iDSProgramForm(void)
		{
			InitializeComponent();
			//
			//TODO: ここにコンストラクター コードを追加します
			//
		}

	protected:
		/// <summary>
		/// 使用中のリソースをすべてクリーンアップします。
		/// </summary>
		~iDSProgramForm()
		{
			if (components)
			{
				delete components;
			}
		}
	private: System::ComponentModel::BackgroundWorker^  SvGraphWork;
	protected: 

	protected: 

	protected: 
	private: System::Windows::Forms::PictureBox^  pictureBox1;
	private: System::Windows::Forms::Timer^  RecordTimer;
	private: System::Windows::Forms::Button^  buttonStart;



	private: System::Windows::Forms::Button^  StopBtn;
	private: System::Windows::Forms::ProgressBar^  progressBar;
	private: System::Windows::Forms::ProgressBar^  RecordBar;
	private: System::Windows::Forms::ProgressBar^  ShutterBar;


	private: System::ComponentModel::IContainer^  components;

	private:
		/// <summary>
		/// 必要なデザイナー変数です。
		/// </summary>


#pragma region Windows Form Designer generated code
		/// <summary>
		/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
		/// コード エディターで変更しないでください。
		/// </summary>
		void InitializeComponent(void)
		{
			this->components = (gcnew System::ComponentModel::Container());
			this->SvGraphWork = (gcnew System::ComponentModel::BackgroundWorker());
			this->pictureBox1 = (gcnew System::Windows::Forms::PictureBox());
			this->RecordTimer = (gcnew System::Windows::Forms::Timer(this->components));
			this->buttonStart = (gcnew System::Windows::Forms::Button());
			this->StopBtn = (gcnew System::Windows::Forms::Button());
			this->progressBar = (gcnew System::Windows::Forms::ProgressBar());
			this->RecordBar = (gcnew System::Windows::Forms::ProgressBar());
			this->ShutterBar = (gcnew System::Windows::Forms::ProgressBar());
			(cli::safe_cast<System::ComponentModel::ISupportInitialize^  >(this->pictureBox1))->BeginInit();
			this->SuspendLayout();
			// 
			// SvGraphWork
			// 
			this->SvGraphWork->WorkerReportsProgress = true;
			this->SvGraphWork->WorkerSupportsCancellation = true;
			this->SvGraphWork->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &iDSProgramForm::SvGraphWork_DoWork);
			this->SvGraphWork->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &iDSProgramForm::SvGraphWork_ProgressChanged);
			this->SvGraphWork->RunWorkerCompleted += gcnew System::ComponentModel::RunWorkerCompletedEventHandler(this, &iDSProgramForm::SvGraphWork_RunWorkerCompleted);
			// 
			// pictureBox1
			// 
			this->pictureBox1->BorderStyle = System::Windows::Forms::BorderStyle::FixedSingle;
			this->pictureBox1->Location = System::Drawing::Point(13, 13);
			this->pictureBox1->Name = L"pictureBox1";
			this->pictureBox1->Size = System::Drawing::Size(640, 480);
			this->pictureBox1->TabIndex = 0;
			this->pictureBox1->TabStop = false;
			// 
			// RecordTimer
			// 
			this->RecordTimer->Interval = 1;
			this->RecordTimer->Tick += gcnew System::EventHandler(this, &iDSProgramForm::RecordTimer_Tick);
			// 
			// buttonStart
			// 
			this->buttonStart->Location = System::Drawing::Point(660, 13);
			this->buttonStart->Name = L"buttonStart";
			this->buttonStart->Size = System::Drawing::Size(75, 23);
			this->buttonStart->TabIndex = 1;
			this->buttonStart->Text = L"撮影";
			this->buttonStart->UseVisualStyleBackColor = true;
			this->buttonStart->Click += gcnew System::EventHandler(this, &iDSProgramForm::buttonStart_Click);
			// 
			// StopBtn
			// 
			this->StopBtn->Location = System::Drawing::Point(787, 13);
			this->StopBtn->Name = L"StopBtn";
			this->StopBtn->Size = System::Drawing::Size(75, 23);
			this->StopBtn->TabIndex = 5;
			this->StopBtn->Text = L"撮影やめ!";
			this->StopBtn->UseVisualStyleBackColor = true;
			this->StopBtn->Click += gcnew System::EventHandler(this, &iDSProgramForm::StopBtn_Click);
			// 
			// progressBar
			// 
			this->progressBar->Location = System::Drawing::Point(660, 49);
			this->progressBar->Name = L"progressBar";
			this->progressBar->Size = System::Drawing::Size(202, 23);
			this->progressBar->TabIndex = 2;
			// 
			// RecordBar
			// 
			this->RecordBar->Location = System::Drawing::Point(660, 79);
			this->RecordBar->Maximum = 400;
			this->RecordBar->Name = L"RecordBar";
			this->RecordBar->Size = System::Drawing::Size(202, 23);
			this->RecordBar->TabIndex = 3;
			// 
			// ShutterBar
			// 
			this->ShutterBar->Location = System::Drawing::Point(660, 109);
			this->ShutterBar->Maximum = 200;
			this->ShutterBar->Name = L"ShutterBar";
			this->ShutterBar->Size = System::Drawing::Size(202, 23);
			this->ShutterBar->TabIndex = 4;
			// 
			// iDSProgramForm
			// 
			this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
			this->ClientSize = System::Drawing::Size(874, 528);
			this->Controls->Add(this->StopBtn);
			this->Controls->Add(this->ShutterBar);
			this->Controls->Add(this->RecordBar);
			this->Controls->Add(this->progressBar);
			this->Controls->Add(this->buttonStart);
			this->Controls->Add(this->pictureBox1);
			this->Name = L"iDSProgramForm";
			this->Text = L"iDSProgramForm";
			this->FormClosed += gcnew System::Windows::Forms::FormClosedEventHandler(this, &iDSProgramForm::iDSProgramForm_FormClosed);
			this->Load += gcnew System::EventHandler(this, &iDSProgramForm::iDSProgramForm_Load);
			(cli::safe_cast<System::ComponentModel::ISupportInitialize^  >(this->pictureBox1))->EndInit();
			this->ResumeLayout(false);

		}
#pragma endregion
	private: System::Void RecordTimer_Tick(System::Object^  sender, System::EventArgs^  e);
	private: System::Void iDSProgramForm_Load(System::Object^  sender, System::EventArgs^  e);
	private: System::Void iDSProgramForm_FormClosed(System::Object^  sender, System::Windows::Forms::FormClosedEventArgs^  e);
	private: System::Void SvGraphWork_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e);
	private: System::Void SvGraphWork_ProgressChanged(System::Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e);
	private: System::Void SvGraphWork_RunWorkerCompleted(System::Object^  sender, System::ComponentModel::RunWorkerCompletedEventArgs^  e);
	private: System::Void buttonStart_Click(System::Object^  sender, System::EventArgs^  e);
	private: System::Void StopBtn_Click(System::Object^  sender, System::EventArgs^  e);
			 
	void DrawCvImage(IplImage *CvImage);
};
}
iDSProgramForm.cpp

コード:

#include "stdafx.h"
#include "iDSProgramForm.h"

#define MEM 400
CvCapture *capture = 0;
IplImage *frame = 0;
IplImage *svframe[MEM] = {0};
int saveCount;
int RecordCount;
int countbuf;
bool RecordFlag;
bool SaveStertFlag;
int count=0;

using namespace iDSProgram;
System::Void iDSProgramForm::iDSProgramForm_Load(System::Object^  sender, System::EventArgs^  e){
	RecordCount = 0;
	saveCount=0;
	capture = cvCreateCameraCapture(1);
	RecordTimer->Enabled = true;
	RecordFlag = false;
	SaveStertFlag = false;
	countbuf=0;
	
	RecordBar->Maximum = MEM;
	ShutterBar->Maximum = MEM/2;
}
System::Void iDSProgramForm::RecordTimer_Tick(System::Object^  sender, System::EventArgs^  e){
	 
	frame = cvQueryFrame (capture);
	DrawCvImage(frame);
	cvWaitKey(10);
	if(RecordFlag){
		svframe[RecordCount] = cvCloneImage(frame);
		RecordBar->Value = RecordCount;
		ShutterBar->Value = saveCount;
		RecordCount++;
		if(RecordCount%(MEM/2) == 0){
			saveCount=0;
			SaveStertFlag=true;
			
			SvGraphWork->RunWorkerAsync(10);
			
		}
		if(RecordCount>MEM)
			RecordCount=0;

	}
}
System::Void iDSProgramForm::buttonStart_Click(System::Object^  sender, System::EventArgs^  e) {
	buttonStart->Enabled = false;
	RecordFlag = true;
	SaveStertFlag = false;
	
}
System::Void iDSProgramForm::StopBtn_Click(System::Object^  sender, System::EventArgs^  e){
	RecordFlag = false;
	SaveStertFlag = false;
}
System::Void iDSProgramForm::iDSProgramForm_FormClosed(System::Object^  sender, System::Windows::Forms::FormClosedEventArgs^  e){
	cvReleaseCapture (&capture);
}

System::Void iDSProgramForm::SvGraphWork_DoWork(System::Object^  sender, System::ComponentModel::DoWorkEventArgs^  e){
	// 別スレッドで実行されるため、このメソッドでは
	// UI(コントロール)を操作してはいけない

	// このメソッドへのパラメータ
	int bgWorkerArg = (int)e->Argument;

	// senderの値はbgWorkerの値と同じ
	BackgroundWorker^ worker = (BackgroundWorker^)(sender);

	// 時間のかかる処理
	while(SaveStertFlag){
		System::Threading::Thread::Sleep(1);
		char fname[255];
		sprintf(fname,"DATA\\%05d.bmp",count);
		cvSaveImage(fname,svframe[saveCount+((countbuf%2)*(MEM/2))]);
		cvReleaseImage(&svframe[saveCount+((countbuf%2)*(MEM/2))]);
		saveCount++;
		count++;
		if(saveCount>(MEM/2)){
			saveCount=0;
			SaveStertFlag = false;
			countbuf++;
		}
		int percentage = saveCount * 100 / (MEM/2); // 進ちょく率
		worker->ReportProgress(percentage);
		// ProgressChangedイベント発生
	}
	

	// この後、RunWorkerCompletedイベントが発生
}

System::Void iDSProgramForm::SvGraphWork_ProgressChanged(System::Object^  sender, System::ComponentModel::ProgressChangedEventArgs^  e){
      progressBar->Value = e->ProgressPercentage;
}
System::Void iDSProgramForm::SvGraphWork_RunWorkerCompleted(System::Object^  sender, System::ComponentModel::RunWorkerCompletedEventArgs^  e){

      buttonStart->Enabled = true;
}


void iDSProgramForm::DrawCvImage(IplImage *CvImage) {
	//IplImageをピクチャボックスへ描画
   
	///////////////////////////////////
	//  Graphicsの確保
	//

	if ((pictureBox1->Image == nullptr)
		|| (pictureBox1->Width != CvImage->width)
		|| (pictureBox1->Height != CvImage->height)){
			//ピクチャボックスをビットマップ画像サイズに合わせる
			pictureBox1->Width = CvImage->width;
			pictureBox1->Height = CvImage->height;
			//PictureBoxと同じ大きさのBitmapクラスを作成する。
			Bitmap^ bmpPicBox = gcnew Bitmap(pictureBox1->Width, pictureBox1->Height);
			//空のBitmapをPictureBoxのImageに指定する。
			pictureBox1->Image = bmpPicBox;
	}

	//Graphicsクラスの作成(空のピクチャボックスからGraphicsを作成する)
	Graphics^g = Graphics::FromImage(pictureBox1->Image);
   
	///////////////////////////////////
	//  IplImageからBitmapの確保
	//     
	//IplImageの画像データのポインタ(imageData)をBitmapへ渡し、Bitmapを作成
	Bitmap^ bmp = gcnew Bitmap(CvImage->width, CvImage->height, CvImage->widthStep,
	System::Drawing::Imaging::PixelFormat::Format24bppRgb, IntPtr(CvImage->imageData));
   
	///////////////////////////////////
	//  画像の描画
	//
	//ピクチャボックスのImageへ
	g->DrawImage(bmp, 0, 0, CvImage->width, CvImage->height); 
	pictureBox1->Refresh();
   
	delete g;
}

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#2

投稿記事 by softya(ソフト屋) » 7年前

C++/CLIは使ったことが無いですがタスクマネージャである程度参考情報が得られると思います。

>・そもそもBackgroundWorkerはマルチスレッドなのか?
MSDNにはスレッドだと書いてあります。

>・原因はHDDのアクセスの問題か?CPUに負荷をかけ過ぎなのか?
タスクマネージャで動作開始時と重くなった時でプロセスのメモリの使用量、CPU使用率、各CPUコアの動作状態を確認してください。
スレッド数も確認したほうが良いでしょう。

>・動作が重くなるのはただ単にスペックが足りていないのか?
プログラム的な問題の可能性が高いと思いますが、プログラム全体が不明なので何とも言えません。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#3

投稿記事 by ホヅミ » 7年前

いつも素早い対応ありがとうございます。
さすがにプログラムの全体像が分からないと思いますのでプロジェクトごとアップロードさせていただきます。
http://www1.axfc.net/uploader/N/so/161845.zip
ID:DXLIB
softya(ソフト屋) さんが書きました: >・原因はHDDのアクセスの問題か?CPUに負荷をかけ過ぎなのか?
タスクマネージャで動作開始時と重くなった時でプロセスのメモリの使用量、CPU使用率、各CPUコアの動作状態を確認してください。
スレッド数も確認したほうが良いでしょう。
タスクマネージャで確認したところ
・メモリは大体3GB中2GB使用(まだバッファメモリとしての余裕あり?)
・CPU使用率は30%は越えず27%行ったり来たり
・各コアの動作状態は4個のコアそれぞれ至って穏やか(スレッドスリープをかけているから?)
といった状態になっております。
スレッド数はよくわかってませんが、プログラムの動きを単純に数えると、
1.カメラで撮影している部分
2.画像を保存している部分+進捗モニター
の二つになってます。

CPUをモニターする限りHDDのアクセスが追いついていないのかな?というのが僕の見解です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#4

投稿記事 by softya(ソフト屋) » 7年前

こっちでコンパイルできそうにないですね。VS2010がまともに使えないので。
VS2012は起動もしないですし、まぁC++/CLIもまともに出来ないんですけどね。VS2012を見る限りマイクロソフトはC++/CLIを止める気でしょうね。

あとHDDアクセスの量はタスクマネージャで確認できます。
表示 → 列の選択 I/O系を追加。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#5

投稿記事 by ホヅミ » 7年前

HDDのアクセスをモニターしてみました。
それでもチンプンカンプンです・・・。
アクセスしすぎな感じですか?
添付ファイル
IOAccess.jpg

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#6

投稿記事 by softya(ソフト屋) » 7年前

大したことが無い様な気がします。
差分を計算するとプロセスのメモリが400MB増加、ディスクが約550MB書き込まれています。
これに掛かった時間が問題ですね。
それと、メモリは増え続けるのですか?それとも約566MBが上限ですか? 重くなった時にも566MB程度でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#7

投稿記事 by ホヅミ » 7年前

保存が入るたびにグングン伸びてくのですが、ある程度行くとプログラムが止まります。
下の画像がちょうどその時です。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#8

投稿記事 by softya(ソフト屋) » 7年前

ホヅミ さんが書きました:保存が入るたびにグングン伸びてくのですが、ある程度行くとプログラムが止まります。
下の画像がちょうどその時です。
それなりに時間が掛かっているのであれば、700MBぐらいの書き込みは問題ないと思います。
ただし、HDDが容量不足・デフラグ不足で極端に分断化とかしていれば別かもしれません。
あとHDDに同時やランダムアクセスするのもマズイです。

CPUの性能はベンチで
Intel Core i7-2700K  P 12543
Core i7-640LM  P 2964
性能がだいぶ差があるのでOSの処理で手間取っているのかも知れません。
http://hardware-navi.com/cpu.php
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#9

投稿記事 by ホヅミ » 7年前

CLIでなくC#、VBに乗り換えたとしても、きっとこの問題にぶつかると思います。
HDDへのアクセスの問題が解決できそうな解説サイトとかあると教えてもらえるとうれしいです。
softya(ソフト屋) さんが書きました:
CPUの性能はベンチで
Intel Core i7-2700K  P 12543
Core i7-640LM  P 2964
性能がだいぶ差があるのでOSの処理で手間取っているのかも知れません。
http://hardware-navi.com/cpu.php
いくらi7といえど、こんなに差があるのですね。

beatle
記事: 1280
登録日時: 8年前
住所: 埼玉
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#10

投稿記事 by beatle » 7年前

ベンチマークしてみたらどうでしょうか。
HDDやSSDなどの速度を計測するベンチマークソフト「CrystalDiskMark」 の使い方

ディスクアクセスを速くしたいなら、SSDにするとか、RAIDでストライピング構成にするとか、RAMディスクにするという手があると思います(いずれもハードウェアの投資が必要です)
もしくは、メモリとCPUに余裕があるので、RAM上で画像を圧縮してからHDDに書き出すのでも効果があるかもしれませんね。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 9年前
住所: 東海地方
連絡を取る:

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#11

投稿記事 by softya(ソフト屋) » 7年前

ソースを解読していないのですが、HDDの書き込みは同タイミングで衝突しているのでしょうか?
あと、スレッド負荷に偏ってメインが回っていないと言うようなことは無いですよね?

ちなみに
>・各コアの動作状態は4個のコアそれぞれ至って穏やか(スレッドスリープをかけているから?)
ですが、
System::Threading::Thread::Sleep(1);
だとほとんどスリープの意味をなしません。10以上は欲しいです。
重くなるとこことですが、このアプリのGUIだけでしょうか?
それとも他のアプリも影響を受けていますか?
アプリのGUIだけならsleepが足らないせいかも知れません。試しに増やしてみては?

>HDDへのアクセスの問題が解決できそうな解説サイトとかあると教えてもらえるとうれしいです。

自分で管理すべき問題なので無いとは思います。
各部分の処理時間を計測してネックとなっているところを先に探しましょう。

【補足】
Process Explorerと言うツールがあります。
「窓の杜 - Process Explorer」
http://www.forest.impress.co.jp/lib/sys ... lorer.html
これを使えばもっと細かい情報が取れますが、使いこなせるかは別の問題です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#12

投稿記事 by ホヅミ » 7年前

スレッドのスリープをかけたらいくらかは改善されました!
とても助かりましたし、いろいろ勉強になりました。
大変お世話になりました。ありがとうございます!

ホヅミ
記事: 110
登録日時: 9年前

Re: 【ファイル】HDD高速アクセス?CPUクールタイム?【連続保存】

#13

投稿記事 by ホヅミ » 7年前

いい感じに画像保存できたので、解決ということにします。

閉鎖

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