I canβt get a speed boost while scaling multiple bitmaps in parallel.
Read the following code demonstrating the problem:
class Program
{
static void Main(string[] args)
{
Bitmap[] images = Enumerable.Range(0, 8).Select(_ => new Bitmap(6000, 4000, PixelFormat.Format24bppRgb)).ToArray();
Bitmap[] scaledImages = Enumerable.Range(0, 8).Select(_ => new Bitmap(600, 400, PixelFormat.Format24bppRgb)).ToArray();
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for(int i = 0; i < images.Length; i++)
{
ImageScaler(images[i], scaledImages[i]);
}
stopwatch.Stop();
Console.WriteLine($"Sequential scaling: {images.Length} images in {stopwatch.ElapsedMilliseconds} ms.");
stopwatch.Restart();
Parallel.For(0, images.Length, i => ImageScaler(images[i], scaledImages[i]));
stopwatch.Stop();
Console.WriteLine($"Parallel scaling: {images.Length} images in {stopwatch.ElapsedMilliseconds} ms.");
Console.ReadKey();
}
private static void ImageScaler(Bitmap source, Bitmap destination)
{
using(Graphics g = Graphics.FromImage(destination))
{
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.DrawImage(source, 0, 0, destination.Width, destination.Height);
}
}
}
I get the following results on my system (recent quad i7):
Sequential scaling: 8 images in 1774 ms.
Parallel scaling: 8 images in 1792 ms.
This is an unexpected result for two reasons:
- Images are quite large (24 megapixels). The overhead of starting multiple threads should not have a significant impact.
- Image scaling, of course, is associated with the processor.
To make sure that I have replaced ImageScaler()in the code above the following algorithm for scaling the main image:
private static unsafe void NaiveImageScaler(Bitmap src, Bitmap dst)
{
BitmapData srcBd = src.LockBits(new Rectangle(0, 0, src.Width, src.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
BitmapData dstBd = dst.LockBits(new Rectangle(0, 0, dst.Width, dst.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
unsafe
{
byte* srcBgr = (byte*)srcBd.Scan0.ToPointer();
byte* dstBgr = (byte*)dstBd.Scan0.ToPointer();
for(int yd=0; yd<dst.Height; yd++)
{
for(int xd = 0; xd < dst.Width; xd++)
{
int bSum = 0, gSum = 0, rSum = 0;
for(int ys = 10*yd; ys < 10*yd+10; ys++)
{
for(int xs = 10*xd; xs < 10*xd+10; xs++)
{
bSum += srcBgr[ys * srcBd.Stride + 3 * xs];
gSum += srcBgr[ys * srcBd.Stride + 3 * xs + 1];
rSum += srcBgr[ys * srcBd.Stride + 3 * xs + 2];
}
}
dstBgr[yd * dstBd.Stride + 3 * xd] = (Byte)(bSum / 100);
dstBgr[yd * dstBd.Stride + 3 * xd + 1] = (Byte)(bSum / 100);
dstBgr[yd * dstBd.Stride + 3 * xd + 2] = (Byte)(bSum / 100);
}
}
}
dst.UnlockBits(dstBd);
src.UnlockBits(srcBd);
}
With this code, I get the following results:
Sequential scaling: 8 images in 660 ms.
Parallel scaling: 8 images in 184 ms.
3,6 , .
, ? System.Drawing, ?
" ":
, Multithreading System.Windows.Graphics . , . , .
, ( GDI) .
, (!):
GDI + .net