関連記事
- なんと!今週の土曜日も仕事してますた.来週の土曜日も仕事臭い.TCPソケット通信をゴリゴリ書いてる 【2025年08月30日(土)】
- 【ChatGPT o4】祝日なのに仕事ーバテすぎー今夜こそ絶命だーモルモルモル 【2025年03月20日(木)】
- 【#CSharp】制御系が動き出したべ 【2025年02月20日(木)】
- 【#CSharp】ひっさしぶりにガッツリなC#案件をやっているぞー 【2025年02月19日(水)】
- 今週もひたすらモノ書き #JAVA 祭りがまだまだ続く そろそろC#を書かせろー 【2024年12月04日(水)】
Athlon64x2にて、行ったマルチスレッドグレースケール処理は、こちら
別系の仕事でやっていた、画像処理プログラム
マルチスレッドでもちろん書いたけど、いまいち、速度が上がらない
んで、つらつら、考えてみたら
スレッド間の終了同期を取るため、Thread.Sleep(1);を入れていたり
for(int j = 1; j <THREADCOUNT; j++)
{
m_ThreadJobFixFL[j] = true; //スレッド処理完了フラグ (0番以外は完了させておく)
m_ThreadJobFL[j] = false; //スレッド処理開始フラグ(0番起動 以外は停止)
}
for(int i = 0; i <1000; i++) //スレッド0番(480ライン処理)のみ起動
{
m_ThreadJobFixFL[0] = false; //スレッド処理完了フラグ (0番以外は完了させておく)
m_ThreadJobFL[0] = true; //スレッド処理開始フラグ(0番起動 以外は停止)
while(IsThreadJobFix() == false ) //全スレッドの処理が完了するまで待機
{
Thread.Sleep(1); //<----このあたりの話
}
}
1msのスリープって、クアッド様の前では、あまりにも長い時間この1msのために、演算能力、一部奪われていた予感
終了同期の取り方、いまいち、よろしくないんで、ちょいと書き換えてみたら
性能、極端に上がったんで、書いておこうか、と
条件は以下のとおり
CPU : Intel Core2Quad Q6600(2.4GHz)
Memory : 2GB
OS : Windows XP Pro
言語: Visual Studio 2005(C#2.0) + .Net FrameWork2.0
処理速度が上がっているので、
前回の1000枚のグレースケール処理から、4000枚に増やしてみた
あと、前回と同じように、1枚の画面を、4分割して、スレッド処理やろうとしたけど
速過ぎて(w、スレッド間の終了同期が取れない事態が発生
なので、アルゴリズムを変更
4000枚のうち、1スレッドに1000枚ずつ処理させるようにしてみたり
結果は以下のとおり
| 画像データ4000回の グレースケール処理を実行 |
リリースコンパイル | (参考) Athlon64x2のときの時間 (4倍してあります) |
|
640 * 480 24ビットピクセル 1スレッド |
18,710ミリ秒 | 46,810ミリ秒 |
|
640 * 480 24ビットピクセル 2スレッド |
9,640ミリ秒 | 31,370ミリ秒 |
|
640 * 480 24ビットピクセル 4スレッド |
5,390ミリ秒 | 29,560ミリ秒 |
Sleep()での終了同期は、あまりよろしくないですわ
マルチスレッドの処理は
リソース間同期も大変だけど、終了同期も大変だわ、こりゃ
高速化させたい処理にて、いかに効率よく、CPUにデータを食わすか
設計屋の腕の見せ所、ですな
コードは4スレッド対応版
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
using System.Diagnostics;
using tube4Capture.Objects;
namespace GrayScaleTest
{
[StructLayout(LayoutKind.Sequential,Pack=4)]
public struct Pixel24
{
public byte B;
public byte G;
public byte R;
}
public partial class Form1 : Form
{
private const int THREADCOUNT = 5;
static public Form1 m_MyObj; //スレッドへ情報を渡すため、自分自身のインスタンスをセット
private Bitmap m_ImageBackup; // 一時データ
private Bitmap m_bmapIn; // 入力データ
private Bitmap m_bmapOut; // 出力データ
private bool m_ThreadOnFL;
private bool[] m_ThreadJobFL;
private bool[] m_ThreadJobFixFL;
private Thread[] m_Thread;
private IntPtr m_SouPT;
private IntPtr m_DesPT;
private int m_Width; // Image サイズ
private int m_Height; // Image サイズ
private int m_ThreadFixCount;
private bool m_CalcJobEnd;
private DateTime m_dt;
private Bitmap m_bd;
private Bitmap m_bs;
private BitmapData m_bDataInS;
private BitmapData m_bDataInD;
private IntPtr m_Scan0_InS; // ピクセルデータの開始アドレス
private IntPtr m_Scan0_InD; // ピクセルデータの開始アドレス
private IntPtr m_Scan0_InS2; // 新しいバッファ1
private IntPtr m_Scan0_InD2; // 新しいバッファ2
public Form1()
{
InitializeComponent();
m_MyObj = this;
}
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Image = null;
m_ThreadOnFL = true;
m_ThreadJobFL = new bool[THREADCOUNT];
m_ThreadJobFixFL = new bool[THREADCOUNT];
for (int i = 0; i < THREADCOUNT; i++)
{
m_ThreadJobFL[i] = false;
m_ThreadJobFixFL[i] = false;
}
m_Thread = new Thread[THREADCOUNT];
m_Thread[0] = new Thread(new ThreadStart(ThreadMethod0));
m_Thread[1] = new Thread(new ThreadStart(ThreadMethod1));
m_Thread[2] = new Thread(new ThreadStart(ThreadMethod2));
m_Thread[3] = new Thread(new ThreadStart(ThreadMethod3));
m_Thread[4] = new Thread(new ThreadStart(ThreadMethod4));
m_Thread[0].Name = "画像処理スレッド0";
m_Thread[1].Name = "画像処理スレッド1";
m_Thread[2].Name = "画像処理スレッド2";
m_Thread[3].Name = "画像処理スレッド3";
m_Thread[4].Name = "画像処理スレッド4";
m_Thread[0].Start();
m_Thread[1].Start();
m_Thread[2].Start();
m_Thread[3].Start();
m_Thread[4].Start();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
m_ThreadOnFL = false;
for(int i = 0; i < 200; i++)
{
Application.DoEvents();
Thread.Sleep(1);
}
}
private void button1_Click(object sender, EventArgs e)
{
string FileName;
if (openFileDialog1.ShowDialog() != DialogResult.OK)
{
return;
}
FileName = openFileDialog1.FileName;
m_ImageBackup = new Bitmap(FileName);
m_Width = m_ImageBackup.Width;
m_Height = m_ImageBackup.Height;
m_bmapOut = new Bitmap(m_Width, m_Height);
m_bmapIn = m_ImageBackup;
pictureBox1.Image = m_bmapIn;
}
private void JobCore_Click(object sender, EventArgs e)
{
m_dt = DateTime.Now;
int min = m_dt.Minute;
int sec = m_dt.Second;
int mili = m_dt.Millisecond;
lblStart.Text = "";
lblEnd.Text = "";
lblJob.Text = "";
lblStart.Text = "開始時間:" + min.ToString() + "_" + sec.ToString() + "_" + mili.ToString();
Refresh();
m_bd = pictureBox1.Image as Bitmap;
m_bs = new Bitmap(m_bmapIn);
m_bDataInS = m_bs.LockBits(new Rectangle(0, 0, m_bs.Width, m_bs.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
m_Scan0_InS = m_bDataInS.Scan0; // ピクセルデータの開始アドレス
m_bDataInD = m_bd.LockBits(new Rectangle(0, 0, m_bd.Width, m_bd.Height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
m_Scan0_InD = m_bDataInD.Scan0; // ピクセルデータの開始アドレス
m_Scan0_InS2 = CommonMethedMemory.MemAlloc(m_bs.Width * m_bs.Height * 3); // 新しいバッファ1
m_Scan0_InD2 = CommonMethedMemory.MemAlloc(m_bd.Width * m_bd.Height * 3); // 新しいバッファ2
CommonMethedMemory.MemCopy(m_Scan0_InS2, m_Scan0_InS, m_bs.Width * m_bs.Height * 3);
m_SouPT = m_Scan0_InS2;
m_DesPT = m_Scan0_InD2;
m_Width = m_bd.Width;
m_Height = m_bd.Height;
m_ThreadFixCount = 0;
m_CalcJobEnd = false;
tmrThreadEndCheck.Enabled = true;
if (sender == button2)
{
for(int j = 1; j < THREADCOUNT; j++)
{
m_ThreadJobFixFL[j] = true; //スレッド処理完了フラグ (0番以外は完了させておく)
m_ThreadJobFL[j] = false; //スレッド処理開始フラグ(0番起動 以外は停止)
}
m_ThreadJobFixFL[0] = false; //スレッド処理完了フラグ (0番)
m_ThreadJobFL[0] = true; //スレッド処理開始フラグ(0番)
}
else
{
m_ThreadJobFixFL[0] = true; //スレッド処理完了フラグ (0番は完了させておく)
m_ThreadJobFL[0] = false; //スレッド処理開始フラグ(0番停止 以外は起動)
for(int j = 1; j < THREADCOUNT; j++)
{
m_ThreadJobFixFL[j] = false; //スレッド処理完了フラグ (0番以外)
m_ThreadJobFL[j] = true; //スレッド処理開始フラグ(0番以外)
}
}
}
//スレッド処理終了時の後始末系タイマ
private void tmrThreadEndCheck_Tick(object sender, EventArgs e)
{
if (m_CalcJobEnd == false) //スレッドが終わるまでは、タイマはすぐ抜ける
{
return;
}
tmrThreadEndCheck.Enabled = false;
CommonMethedMemory.MemCopy(m_Scan0_InD, m_Scan0_InD2, m_bs.Width * m_bs.Height * 3);
CommonMethedMemory.MemFree(m_Scan0_InS2);
CommonMethedMemory.MemFree(m_Scan0_InD2);
m_bd.UnlockBits(m_bDataInD);
m_bs.UnlockBits(m_bDataInS);
DateTime dt2 = DateTime.Now;
int min = dt2.Minute;
int sec = dt2.Second;
int mili = dt2.Millisecond;
lblEnd.Text = "終了時間:" + min.ToString() + "_" + sec.ToString() + "_" + mili.ToString();;
TimeSpan tm = dt2.Subtract(m_dt);
lblJob.Text = "経過時間:" + tm.TotalSeconds.ToString();
Refresh();
}
//すべてのスレッドの処理が完了したらTrue
private bool IsThreadJobFix()
{
for(int i = 0; i < THREADCOUNT; i++)
{
if (m_ThreadJobFixFL[i] == false)
{
return false;
}
}
return true;
}
private static void ThreadMethod0() { m_MyObj.ThreadMainJob0(); } //スレッド向けコールバック関数(0番)
private static void ThreadMethod1() { m_MyObj.ThreadMainJob1(); } //スレッド向けコールバック関数(1番)
private static void ThreadMethod2() { m_MyObj.ThreadMainJob2(); } //スレッド向けコールバック関数(2番)
private static void ThreadMethod3() { m_MyObj.ThreadMainJob3(); } //スレッド向けコールバック関数(3番)
private static void ThreadMethod4() { m_MyObj.ThreadMainJob4(); } //スレッド向けコールバック関数(4番)
//スレッドによるグレースケール処理
private void ThreadMainJob0()
{
while(m_ThreadOnFL == true)
{
if (m_ThreadJobFL[0] == false)
{
Thread.Sleep(1);
continue;
}
for(int i = 0; i < 4000; i++)
{
GrayScaleB(0, 480, m_SouPT, m_DesPT, m_Width, m_Height);
}
m_ThreadJobFL[0] = false;
m_ThreadJobFixFL[0] = true;
ThreadJobFixSingle(0);
}
}
private void ThreadMainJob1()
{
while(m_ThreadOnFL == true)
{
if (m_ThreadJobFL[1] == false)
{
Thread.Sleep(1);
continue;
}
for(int i = 0; i < 1000; i++)
{
GrayScaleB(0, 480, m_SouPT, m_DesPT, m_Width, m_Height);
}
m_ThreadJobFL[1] = false;
m_ThreadJobFixFL[1] = true;
ThreadJobFixMulti(1);
}
}
private void ThreadMainJob2()
{
while(m_ThreadOnFL == true)
{
if (m_ThreadJobFL[2] == false)
{
Thread.Sleep(1);
continue;
}
for(int i = 1000; i < 2000; i++)
{
GrayScaleB(0, 480, m_SouPT, m_DesPT, m_Width, m_Height);
}
m_ThreadJobFL[2] = false;
m_ThreadJobFixFL[2] = true;
ThreadJobFixMulti(2);
}
}
private void ThreadMainJob3()
{
while(m_ThreadOnFL == true)
{
if (m_ThreadJobFL[3] == false)
{
Thread.Sleep(1);
continue;
}
for(int i = 2000; i < 3000; i++)
{
GrayScaleB(0, 480, m_SouPT, m_DesPT, m_Width, m_Height);
}
m_ThreadJobFL[3] = false;
m_ThreadJobFixFL[3] = true;
ThreadJobFixMulti(3);
}
}
private void ThreadMainJob4()
{
while(m_ThreadOnFL == true)
{
if (m_ThreadJobFL[4] == false)
{
Thread.Sleep(1);
continue;
}
for(int i = 3000; i < 4000; i++)
{
GrayScaleB(0, 480, m_SouPT, m_DesPT, m_Width, m_Height);
}
m_ThreadJobFL[4] = false;
m_ThreadJobFixFL[4] = true;
ThreadJobFixMulti(4);
}
}
//スレッド演算終了時に呼び出される(シングル)
private void ThreadJobFixSingle(int threadNo)
{
m_CalcJobEnd = true; //シングルスレッド処理
}
//スレッド演算終了時に呼び出される(マルチ)
private void ThreadJobFixMulti(int threadNo)
{
m_ThreadFixCount++; //マルチスレッド処理
if (m_ThreadFixCount < 4) //4スレッドが終了するまで最終処理を行わない
{
return;
}
m_CalcJobEnd = true;
}
//グレースケール処理
public void GrayScaleB(int startY, int endY, IntPtr ptS, IntPtr ptD, int tmpWidth, int tmpHeight)
{
int x, y;
int index;
byte PixelData;
byte red, green, blue;
int scanl = tmpWidth * 3;
unsafe
{
byte* ps = (byte*)ptS;
byte* pd = (byte*)ptD;
for (y = startY; y < endY; y++)
{
for (x = 0; x < scanl; x += 3)
{
index = (y * scanl) + x;
blue = ps[index ];
green = ps[index + 1];
red = ps[index + 2];
PixelData = (byte)(0.299f * ((float)red) + 0.587f * ((float)green) + 0.114f * ((float)blue));
pd[index] = PixelData;
pd[index + 1] = PixelData;
pd[index + 2] = PixelData;
}
}
}
}
}
}

コメント
【#HTVX】新型宇宙輸送船HTV-X 油井さんがロボットアームでキャッチ
【冬】来週11℃予想とか出てきてる.秋があったのか,今年(25/10/23)
【ツル】出水平野に冬の訪れを告げるツル第一陣・今季初飛来 鹿児島(25/10/21)
都城でプロ野球・ロッテ1軍が春季キャンプ実施へ
山形で開催の映画祭 監督など国内外の参加者が“芋煮”味わう
連休なんで生駒高原でコスモスを愛でてきた
連休の中日ですの.天気は良くなる方向じゃげ
【大リーグ】大谷翔平 先頭打者HR ワイルドカードシリーズ
10月に入りましたなぁ.今年もあと90日ちょっと,早い!