2010年9月25日土曜日

[.NET]いまさら?Parallel.Forを使ってみた(その1)

簡単に平行処理ができる、Parallel.Forというクラスが.NET4で提供されています。で、以前作成した、GDD Blog: [.NET]画像処理グレースケール化(1)の処理をParallel.Forにしたら速くなるか?とおもってちょっと実験。小さい画像だと差が出ずらいので、ちょっと大きめの画像(3,008×2,000)で上記リンクの処理を5回動作させてみると、平均で16.32秒でした。タスクマネージャでCPUの稼動を見ているとほぼ50%で停滞。というか1コアしか使っていない。

で、以下のコードで実行してみると、CPU稼動率は100%で張り付いたものの、5回の平均を計ると18.297秒。なんと10%くらい性能が悪化していました。BitmapのGetPixおよびSetPixはマルチスレッドに対応していないようで、結果的に同期のコストがかかることが原因ということがわかりました。

で、残念なコードは以下のとおり。
private void btnGlay2_Click(object sender, EventArgs e)
{
    textBox1.Text = "グレースケール化(2)";

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    Bitmap bmp = new Bitmap(ctlPicture.Image);
    int h = bmp.Height;
    int w = bmp.Width;
    Parallel.For(0, w, delegate(int i)
    {
        for (int j = 0; j < h; j++)
        {
            Color color;
            lock (bmp)
            {
                color = bmp.GetPixel(i, j);
                // 計算式にて、値を算出してセットする
                //int col = (int)(color.R * 0.299 + color.G * 0.587 + color.B * 0.114);
                //int col = (int)((color.R + color.G + color.B) / 3);
                int col = (int)Math.Round((color.R + color.G + color.B) / 3.0d);
                Color pix = Color.FromArgb(col, col, col);
                bmp.SetPixel(i, j, pix);
            }
        }
    });
    ctlPicture.Image = bmp;

    sw.Stop();
    textBox1.Text = String.Format(textBox1.Text + " 完了{0:#,##0}ms", sw.ElapsedMilliseconds);
}

0 件のコメント: