Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel For loop in C# with shared variable

I'm trying to use parallel processing to speed up a couple of nested loops, but I'm having trouble getting the syntax right. I'm trying to get a count of how many pixels in a bitmap are either red, white, or black, the values for which I have in an enum elsewhere.

In serial processing I have the following code, which works fine:

        Bitmap image = new Bitmap(@"Input.png");
        var colourCount = new int[3];

        for (var x = 0; x < image.Width; x++)
        {
            for (var y = 0; y < image.Height; y++)
            {
                switch (image.GetPixel(x, y).ToArgb())
                {
                    case (int)colours.red: colourCount[0]++; break;
                    case (int)colours.white: colourCount[1]++; break;
                    case (int)colours.black: colourCount[2]++; break;
                    default: throw new ArgumentOutOfRangeException(string.Format("Unexpected colour found: '{0}'", image.GetPixel(x, y).ToArgb()));
                }
            }
        }

I've seen code for parallel for loops by Microsoft and from Stackoverflow that update a shared variable such as below:

        Parallel.For<int>(0, result.Count, () => 0, (i, loop, subtotal) =>
        {
            subtotal += result[i];
            return subtotal;
        },
            (x) => Interlocked.Add(ref sum, x)
        );

But all the examples use a simple type such as an int as the shared variable and I just can't figure out the syntax to write to my size three array. Am I approaching this all wrong?

By the way, I know in terms of performance that GetPixel is very slow compared to something like Bitmap.LockBits, I'm just trying to get the principle of parallel loops right.

like image 752
Andrew Jones Avatar asked Jun 14 '13 22:06

Andrew Jones


People also ask

What is a parallel for loop?

The Parallel. For loop executes iterations that may run in parallel. You can monitor and even manipulate the state of the loop. The Parallel. For loop is just like the for loop except it allows the iterations to run in parallel across multiple threads.

What is a parallel program in C?

Parallel programming is the process of using a set of resources to solve a problem in less time by dividing the work. Using parallel programming in C is important to increase the performance of the software.

What is parallel for loop in C#?

Parallel. For() can improve performance a lot by parallelizing your code, but it also has overhead (synchronization between threads, invoking the delegate on each iteration).

Does C support parallel execution?

C++17 added support for parallel algorithms to the standard library, to help programs take advantage of parallel execution for improved performance.


1 Answers

You can use an overload of Parallel.For that permits you to maintain thread-local state. In this case, we create an int[3] array for each thread that is spawned. Within each iteration of the parallel loop, we only update the local array, localColourCount. Finally, when the thread is to be retired, we aggregate the results of each local array into the global one, colourCount; however, since this is a shared data structure, we enforce mutual exclusion whilst accessing it.

Bitmap image = new Bitmap(@"Input.png");
var colourCount = new int[3];

Parallel.For(0, image.Width,

    // localInit: The function delegate that returns the initial state
    //            of the local data for each task.
    () => new int[3],

    // body: The delegate that is invoked once per iteration.
    (int x, ParallelLoopState state, int[] localColourCount) =>
    {
        for (var y = 0; y < image.Height; y++)
        {
            switch (image.GetPixel(x, y).ToArgb())
            {
                case (int)colours.red: localColourCount[0]++; break;
                case (int)colours.white: localColourCount[1]++; break;
                case (int)colours.black: localColourCount[2]++; break;
                default: throw new ArgumentOutOfRangeException(
                             string.Format("Unexpected colour found: '{0}'", 
                             image.GetPixel(x, y).ToArgb()));
            }
        }
    },

    // localFinally: The delegate that performs a final action
    //               on the local state of each task.
    (int[] localColourCount) =>
    {
        // Accessing shared variable; synchronize access.
        lock (colourCount)
        {
            for (int i = 0; i < 3; ++i)
                colourCount[i] += localColourCount[i];
        }
    });

This code assumes that Bitmap.GetPixel is thread-safe, which may or may not be the case.

Another thing you need to watch out for is that any ArgumentOutOfRangeException instances will get combined into an AggregateException, so you'll need to adjust your error-handling code.

like image 153
Douglas Avatar answered Oct 25 '22 19:10

Douglas