C# .NET 4.0 Using Parallel.ForEach To Speed Up Loops

By Indranil Chatterjee

Often we perform a common operation over a sequence of elements by looping through them one at a time. If the operation is expensive and there aren't any dependency or context to be managed or carried across the elements, then we can drastically improve performance by running them parallelly on multiple cores/processors. This example illustrates how to acheive it using simple constructs of .NET 4.0.

Most of our machines today have multiple cores/processors, so why not take advantage of them. While we can do that using traditional multithreading, but it involves a lot of low level programming and it's the developer's headache to appropriately divide the source data, create the threads, manage synchronization etc.

This example illustrates the use of Parallel.ForEach method (provided by .NET 4.0) to manipulate a bunch of images(typically a resource intensive operation):

Note: Add a reference to System.Drawing.dll before executing the program.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Drawing;
using System.Diagnostics;

namespace ParallelProgramming.TaskPrograms
{
//Dump some arbitrary number of images to this folder or any other folder before running the program
//The program then manipulates the images by using a sequential and a parallel loop and displays both results
//which you can compare

public static class DataParallelismProgram
{
private const string _newDir = @"C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures\Modified";
private const string _existingDir = @"C:\Documents and Settings\All Users\Documents\My Pictures\Sample Pictures";
private static Stopwatch _watch = new Stopwatch();

public static void Execute()
{
ParseImages();
ParseImagesInParallel();
}

//The sequential loop
private static void ParseImages()
{
_watch.Restart();
string[] files = Directory.GetFiles(_existingDir, "*.jpg");
ResetFolder();
foreach (var file in files)
{
var bitMap = new Bitmap(file);
bitMap.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitMap.Save(Path.Combine(_newDir, Path.GetFileName(file)));
}
_watch.Stop();
Console.WriteLine("Time taken by serial method: {0} milliseconds", _watch.ElapsedMilliseconds);
}

//The parallel loop
private static void ParseImagesInParallel()
{
_watch.Restart();
string[] files = Directory.GetFiles(_existingDir, "*.jpg");
ResetFolder();
Parallel.ForEach(files, file =>
{
var bitMap = new Bitmap(file);
bitMap.RotateFlip(RotateFlipType.Rotate180FlipNone);
bitMap.Save(Path.Combine(_newDir, Path.GetFileName(file)));
});
_watch.Stop();
Console.WriteLine("Time taken by parallel method: {0} milliseconds", _watch.ElapsedMilliseconds);
}

private static void ResetFolder()
{
if (Directory.Exists(_newDir))
Directory.Delete(_newDir, true);
Directory.CreateDirectory(_newDir);
}
}

class Program
{
static void Main(string[] args)
{
DataParallelismProgram.Execute();
}
}
}

C# .NET 4.0 Using Parallel.ForEach To Speed Up Loops  (4697 Views)