関連記事
- 【#CSHARP】ソリューション内でサービス,Winformアプリを混在で作成させ,バイナリ出力先を同じにするとサービスが起動しない 【2024年02月08日(木)】
- 【#PHP】謎な #Laravel 案件 引き渡したじ 【2024年01月11日(木)】
- 【C#】謎な改造アプリ,SIMD命令を導入して高速化しようか,思案中 【2023年11月14日(火)】
- Javaの”やらかし”でC#と人気逆転か 激変プログラミング言語人気ランキング 【2023年10月18日(水)】
- 【#CSHARP】次の案件のスケルトン書いてgitサーバに登録するなどと 【2023年05月16日(火)】
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; } } } } } }
コメント
【#閃輝暗点】閃輝暗点が始まった!いつもどおり目が見えなくなってるー(汗
【#イプシロン】イプシロンSロケット 燃焼試験中に爆発事故発生
あっちもこっちも風邪が流行ってるの.外に出たら感染する勢い!
【訃報】俳優 火野正平さん死去 75歳
【地震】日向灘でM4.4の地震
【#第三次世界大戦】ウクライナ空軍 “ICBM1発がロシア南部から発射された”
司法省 “グーグルはクローム売却を” 裁判所に要求へ 米報道
最近,仕事がずっと激しい...ドロドロドロ
今日は謎の宴会らしい...スーツが入るのかっ!?(GRB爆
「侍ジャパン」宮崎県入り 「プレミア12」向け きょうから合宿