Parallel Managed Code - TPL Analogue for .Net 2.0

Here is a simple, minimal, but efficient solution to supporting multi-core processors in your software with older versions of the .NET framework.

Introduction


In case you haven't noticed, computer processors aren't getting faster any more. There was a time when every few months a new range of chips would be available from Intel, or AMD, and their friends, boasting new faster processing. Nowadays, we can still get new chips but now they provide improvements other than raw speed increases.

To take advantage of the new "multi-core" chips, we need to write our software a bit differently. This is quite straightforward when using modern programming languages and frameworks, but less so for legacy systems. For example, current versions of Microsoft's .Net framework offers something known as the Parallel Task Library. It is lovely. The world is great. We are all happy. Well, that is all true until we write code with a slightly older, but still commonly used version of the .NET framework.

How can we leverage current and future CPU architectures, while still targeting the .NET 2.0 framework? We can manually manage threads in our code. Yuck! There's no way we want to do that! When recently confronted with this problem I decided to take a slightly different approach. I cloned a minimal subset of the Task Parallel Library, and I'd like to share the fruits of my labours here. With you. No ties.

An underlying requirement of the following code is to support .NET 2.0, and above, and Mono 2.2 and above (and it almost certainly works with earlier versions of Mono but that is just a pleasant coincidence).

Background


The normal key to improving software performance on modern computing architectures is to run on multiple processors, or processing cores, in parallel. Microsoft's Task Parallel Library (TPL) is designed to make it easy to develop managed code that automatically uses multiple processors. TPL does not require any particular language extensions and works with the .NET Framework version 3.5 and later.

A really good introductory article about these topics is available from Microsoft: "Optimize Managed Code For Multi-Core Machines".

Unfortunately, the TPL requires the .NET Framework version 3.5, or later.

Solution


I won't describe how the code works in any great detail, as you can read that from the source code itself. A Visual Studio solution is provided, and this should work on Visual Studio 2005 and later versions. It probably also works with Novell's MonoDevelop.

If you wish to run the included unit tests, you'll need the NUnit unit testing framework. I've tested with NUnit 2.4.5, but other version will probably be equally as good. If you don't have NUnit, just exclude the ParallelTests project from the solution in Visual Studio. Apart from that optional dependency, this code is completely standalone, just requiring a C# 2.0 compiler, whether from Microsoft, Mono, or anywhere else.

I have only implemented equivalents of the Parallel.For and Parallel.ForEach looping constructs, but these are typically sufficient for parallelising code in the most obvious way. In both cases, my versions are drop-in replacements for the delegate forms of the constructs in the TPL. Therefore, it should be trivial to "upgrade" to the official TPL at a later date.

To use this within your own code, you can simply duplicate the main Parallel.cs file in your project and then use the Parallel class directly, or you can use my Uk.Org.Adcock.Parallel assembly, as is, and reference the Parallel class from within that.

The included unit tests can act as full examples, if you need then, but it is quite simple...

Stealing an example from the MSDN article linked above, suppose you wanted to square the elements in an array, a, e.g.:

for (int i = 0; i < 100; i++)
{
  a[i] = a[i] * a[i];
}

The individual iterations are independent of each other, i.e., subsequent iterations do not depend on prior iterations, you can utilise parallelism with a call to the Parallel.For method, e.g.:

Parallel.For(0, 100, delegate(int i)
{
  a[i] = a[i] * a[i];
});

It really is that simple!

The Parallel.For method is implemented similarly, but I won't insult your intelligence by providing an example. Look at the unit tests if you really need one.

I also allow simple configuration of the maximum number of threads to utilise. By default, however, one thread per CPU core reported by the operating system will be used. Simply set the value of the Parallel.ThreadCount property, where a value of zero indicates that one thread per available CPU core should be used.

Availability and License


I am making this code available freely, under the terms of the 2-clause BSD license.

The full source code is available here: TPL Analogue for .NET 2.0 and later

0 comment