C#4.0(Visual Studio 2010)からはParallel.For(名前空間:System.Threading.Tasks)による並列処理が可能となります。
2コアや4コアは当たり前の時代なので、C#のParallel.Forを使った方が良いかは別としても、並列処理はしないとCPUの無駄遣い状態になってしまいます。
Parallel.Forの構文については、定番の下記 ++C++のページ↓
並列処理ライブラリ
概要 Ver. 4.0 マルチコア CPU の普及に伴って、並列処理の重要性が増しています。 この時代背景に合わせるかのように、.NET Framework 4で並列処理用のライブラリが追加されました。 Para…
を見て頂くと分かりやすいかと思いますが、通常のfor文では
for (int i = 0; i < N; i++)
{
// 通常の処理
}
と書くところを
Parallel.For(0, N, i =>
{
// この部分の処理が並列化される
});
のように書くと、Parallel.Forの{ }内では並列処理が行われます。
ここでは、Parallel.Forを画像処理的に使うと、どうなるか?やってみます。
まず、通常のfor文でコントラスト調整をする以下のようなメソッドを作ります。
private void ContrastImage(Bitmap bmp, double scale, double offset)
{
var width = bmp.Width;
var height = bmp.Height;
// Bitmapをロック
var bmpData = bmp.LockBits(
new Rectangle(0, 0, width, height),
System.Drawing.Imaging.ImageLockMode.ReadWrite,
bmp.PixelFormat
);
// メモリの幅のバイト数を取得
var stride = Math.Abs(bmpData.Stride);
int channel = Bitmap.GetPixelFormatSize(bmp.PixelFormat) / 8;
// 画像データ格納用配列
var picData = new byte[stride * bmpData.Height];
// Bitmapデータを配列へコピー
System.Runtime.InteropServices.Marshal.Copy(
bmpData.Scan0,
picData,
0,
stride * bmpData.Height
);
for (int y = 0; y < height; y++)
{
int lineIndex = stride * y;
for (int x = 0; x < width * channel; x++)
{
int value = picData[lineIndex + x];
value = (int)(value * scale + offset);
value = (value < 0) ? 0 : (value > 255) ? 255
: value
;
picData[lineIndex + x] = (byte)value;
}
}
// 配列をBitmapデータへコピー
System.Runtime.InteropServices.Marshal.Copy(
picData,
0,
bmpData.Scan0,
stride * bmpData.Height
);
// アンロック
bmp.UnlockBits(bmpData);
}
このメソッドの二重ループの部分
for (int y = 0; y<height; y++)
{
int lineIndex = stride * y;
for (int x = 0; x<width* channel; x++)
{
int value = picData[lineIndex + x];
value = (int)(value* scale + offset);
value = (value< 0) ? 0 : (value > 255) ? 255
: value
;
picData[lineIndex + x] = (byte)value;
}
}
をParallel.Forを使って並列処理にすると
Parallel.For(0, height, y =>
{
int lineIndex = stride * y;
for (int x = 0; x<width* channel; x++)
{
int value = picData[lineIndex + x];
value = (int)(value* scale + offset);
value = (value< 0) ? 0 : (value > 255) ? 255
: value
;
picData[lineIndex + x] = (byte)value;
}
}
);
となります。
実際に、このメソッドを使って処理を行い、5回の平均処理時間を比べたところ
通常のfor文 | Parallel.For |
197.2msec | 92.4msec |
CPU:i7-7700K@4.2GHz(4コア)
処理画像サイズ:6000 x 4000 x 24bitカラー
という結果になりました。
評価ソフトのイメージ
(処理前) | (処理後) |
Parallel.Forを使うとCPUのコア数分ぐらいは速くなるのか?と期待していたのですが、他の処理を行っても、だいたい2倍ちょっと速くなるようです。
また、Parallel.Forを使い始めたばかりだとやってしまいがちな定番のミスですが、
ここでのサンプルの lineIndex や value をParallel.Forよりも前で定義してしまうと、正しい結果を得られなくなるので、ご注意下さい。
← C#2.0からの脱却 へ戻る
コメント