Introduction to Parallel Programming in the .NET Framework

Why Parallel Programming

Historically Moore's law has allowed us to largely ignore writing code designed to execute in parallel because the processor speed has continued to dramatically increase over time allowing our applications to run faster based solely on the hardware. Hardware has become increasingly more powerful and more readily available. However, clock speeds have leveled out now around 2.5 GHz. In order to provide increased performance, engineers have started adding more cores to the processor and more processors to each system. With more cores available, it means to get the next level of performance from your applications you would have previously gotten by purchasing a faster processor you will likely need to resort to writing code designed to use the multiple cores and execute in parallel rather than sequentially. Parallel programming, also commonly referred to as concurrent programming, is about writing code designed to execute multiple steps at the same time rather than single steps sequentially.

Think of parallel code as being similar to threading and running multiple threads, but the difference being they are also executing across different processors simultaneously. Microsoft released some items in the .NET Framework as well as in Microsoft Visual Studio that allows you to do more parallel programming and split tasks between processors. As a cautionary note, just because it is available and can be faster doesn't mean that everything should be done in parallel. There are no guarantees made about the order in which the operations will execute. Often times you can get different results depending upon the problem you are solving.

The Parallel class has a number of static methods for performing multiple operations in parallel. We'll focus the remainder of this article on taking a look at some of the static methods.

Parallel.Invoke

The Parallel.Invoke is a static method takes a parameter array of System.Action objects indicating what actions to execute in parallel. Each parameter is launched as a separate parallel task. It will work with methods, anonymous delegates, and lamda expressions as the input. The following sample demonstrates the concept of reading the contents of a file and then calling several static methods in parallel.

static void Main(string[] args)
{
string[] inputFiles = { "c:\\test1.txt", "c:\\test2.txt" };
foreach (string file in inputFiles)
{
string fileContent = System.IO.File.ReadAllText(file);
Parallel.Invoke(
() => CountCharacters(fileContent),
() => CountWords(fileContent),
() => CountParagraphs(fileContent),
() => ProcessFile(fileContent)
);
}
}

static void CountCharacters(string content)
{
// implementation here
}

static void CountWords(string content)
{
// implementation here
}

static void CountParagraphs(string content)
{
// implementation here
}

static void ProcessFile(string content)
{
// implementation here
}



Introduction to Parallel Programming in the .NET Framework

Parallel.For

Code that can benefit from parallel execution commonly involves a looping construct. Parallel.For is a static method on the Parallel class that provides support for parallel looping. Before looking at a parallel loop, we'll first look at a non-parallel loop example to set a foundation for comparison. The following sample code demonstrates a console application to find prime numbers and display information to the console. We'll then compare the sequential mechanism to a parallel one.

static void Main(string[] args)
{
int maxPrimes = 1000000;
int maxNumber = 20000000;
long primesFound = 0;
Console.WriteLine("Sequential Approach");
System.Diagnostics.Stopwatch watch = new Stopwatch();
watch.Start();
for (UInt32 i = 0; i < maxNumber; ++i)
{
if (IsPrimeNumber(i))
     {
     		Interlocked.Increment(ref primesFound);
          	if (primesFound > maxPrimes)
           {
           	Console.WriteLine("Last prime found: {0:N0}", i);
                break;
           }
     }
}

// Output timer info and wait
watch.Stop();
Console.WriteLine("Found {0:N0} primes in {1}", 
primesFound, watch.Elapsed);
Console.ReadKey();
}

public static bool IsPrimeNumber(UInt32 number)
{
// check evenness
if (number % 2 == 0)
{
if (number == 2)
             		return true;
             return false;
}

       UInt32 max = (UInt32)Math.Sqrt(number);
       for (UInt32 i = 3; i <= max; i += 2)
       {
       	if ((number % i) == 0)
             {
             		return false;
          	}
}
      	return true;
}

Here is sample output from the sequential method that shows the highest prime number found along with information about how long the algorithm took to execute. The IsPrimeNumber method is called inside the loop structure.

[New Image.JPG]
Figure 1

Now we'll refactor the example above to use the Parallel.For construct so the search for prime numbers. The IsPrimeNumber method will not change, so I'll omit it from the example below for brevity. The primary difference between the examples is the second example employs the Parallel.For construct. Parallel.For is a normal static method with three parameters. The first argument specifies the starting point of the loop. The second argument specifies the loop size. The third argument is a delegate function called during each iteration of the loop.

static void Main(string[] args)
{
int maxPrimes = 1000000;
int maxNumber = 20000000;
long primesFound = 0;
Console.WriteLine("Parallel Approach");
System.Diagnostics.Stopwatch watch = new Stopwatch();
      	watch.Start();
      	Parallel.For(0, maxNumber, (i, loopState) =>
{
if (IsPrime((UInt32)i))
{
Interlocked.Increment(ref primesFound);
if (primesFound > maxPrimes)
                    {
                        Console.WriteLine("Last prime found was: {0:N0}", i);
                        loopState.Stop();
                    }
}
});

// Output timer info and wait
watch.Stop();
Console.WriteLine("Found {0:N0} primes in {1}", 
primesFound, watch.Elapsed);
Console.ReadKey();
}

Figure 2 below shows the output from executing in parallel.

[New Image2.JPG]
Figure 2

The computer on which I ran the example has a processor with multiple cores. As you can see from the timer output, the parallel version executes several seconds faster. However, you should also notice the outcome isn't the same as what was obtained in the sequential example because there are no guarantees made about the order in which the operations execute. Each time you execute the parallel version you are likely to get different results.

Additional Uses

It was noted earlier in the article that not all items should be implemented in parallel. I wanted to share a few instances where I've personally seen parallel programming implemented and providing value. The following examples represent a couple of instances where I've found parallel to be helpful.

  • Reading independent data values from a database to populate a form. The key to note is the independent. If you are reading data for a grid-like display in a particular order, then parallel is probably not a good option. However, if you are reading a bunch of independent data values to display in a variety of controls, then parallel may be a good option.
  • Processing individual lines from a data file. The contents of the entire data file are read sequentially all at once using method of System.IO.File. The individual lines can be processed in parallel or multiple actions performed on the file contents in parallel.

Summary

We looked at the background behind parallel programming and the importance it holds as systems grow performance-wise through multiple cores. We explored the static methods Parallel.Invoke and Parallel.For and looked at samples. We wrapped up with a look at some additional uses from my personal experiences. Hopefully this has given you an idea of some of the parallel capabilities in the .NET framework and you can explore uses in your own applications.

Future Columns

The topic of the next column is yet to be determined. If you have something else in particular that you would like to see explained here you could reach me at through http://markstrawmyer.com.





About the Author

Mark Strawmyer

Mark Strawmyer is a Senior Architect of .NET applications for large and mid-size organizations. He specializes in architecture, design and development of Microsoft-based solutions. Mark was honored to be named a Microsoft MVP for application development with C# for the fifth year in a row. You can reach Mark at mark.strawmyer@crowehorwath.com.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds