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.
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.
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.
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).
C++17 added support for parallel algorithms to the standard library, to help programs take advantage of parallel execution for improved performance.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With