14.1.1 Parallelization of Imperative code
In imperative programming, a For loop can be the most common structure that is easily parallelized. When the Loop iterator is independent (independent), it can be executed on a separate thread. That is, because of independence, iterators do not rely on values computed by any of the previous iterators.
For example, when you are counting elements in an array, to calculate the next element, you need to count the sum of the preceding elements. (This can still be parallel, but not so simple.) Recall the function of the "fuzzy" array we implemented in chapter tenth, which is ideal for parallelization: Although multiple elements of an input array are used each iteration, it does not rely on any element in the output array. Listing 14.1 shows a simple for loop based on the previous example, containing C # and F # two versions.
Listing 14.1 Calculating a fuzzy array with a For loop (C # and F #)
c# F # for (int i=1 ; i<inp. Length-1 ; i++) {var sum = Inp[i-1 ] + inp[i] + Inp[i+1 ]; Res[i] = sum /3 ; } for i in 1 .. Inp. Length-2 do let Sum = InP. [I-1 ] + INP. [i] + INP. [I+1 ] res.[i] <-sum /3
Although this is imperative code, it can still be part of a purely functional program. INP is an input array that cannot be modified anywhere in the code, and res is an output array that should not be modified after the loop is calculated.
To parallelize a loop, you can use the Parallel.For method. This class is available only on. NET 4.0 under the System.Threading.Tasks namespace. The arguments to the Parallel.For method are the Action delegate values, which can be provided by the lambda function. In F #, using this method directly feels a bit overqualified, so we'll define a simple function that makes the code more concise:
let pfor nfrom nto f = Parallel.For1, Action<_>(f)) |> ignore
This code wraps the function f (the unit with the type int) in the delegate type, running a for loop in parallel. method returns information about whether the loop completed successfully, but we do not need it, so we ignore it. Note that we also add 1 to the upper limit because the For loop in F # is an inclusive cap, and the C # for loop and Parallel.For methods do not contain caps. Listing 14.2 shows the parallelized version of the previous example.
Listing 14.2 Parallel folding for loops (C # and F #)
C # F #
Parallel.For(1,inp.Length-1{ var sum = inp[i-1] + inp[i] + inp[i+1]; res[i] = sum / 3; }1 (inp.Length-2) (fun i –> let sum = inp.[i-1] + inp.[i] + inp.[i+1] 3 )
It can be found that this is almost as simple as the original serial version. It also demonstrates the power of a functional construct: because of the lambda function, the only thing to do is to use the Parallel.For method (or use the Pfor function in F #) instead of the built-in language constructs, since the serial for loop is converted to parallel.
Attention
In the Parallel class, in addition to the For method, there is a foreach method that can be used to parallelize the foreach construct in C #, or the for ... in. "Do construct in F #. Both of these methods can be overloaded with custom iterators. Overloading can change the step size, increase the index in the for method, or stop parallel execution (similar to the loop break breaks in C #). If you feel you need a little more control, please refer to the documentation to see if there are overloads to help you.
The Parallel.For method is particularly useful for working with arrays and other imperative data structures. At the back of this chapter (14th. 2.5), we'll use it again in a larger sample application to manipulate the array in a functional way. Now, let's end the introduction, and the two techniques discussed below are purely functional.
14.1.1 Parallelization of Imperative code