Reputation: 77
I am trying to make my quick sort program in F# work in parallel by making two tasks execute in parallel. I tried looking at Microsofts online documentation but it didn't really help me! Here is my code without parallelism:
let rec quicksort (list: int list) =
match list with
| [] -> [] // if empty list, yield nothing
// otherwise, split the list into a head and tial, and the head is the pivot value
| pivot :: tail ->
// Using List.partition to partition the list into lower and upper
let lower, upper = List.partition (fun x -> x < pivot) tail
// Recursive calls, final product will be low list + pivot + high list
quicksort lower @ [pivot] @ quicksort upper
I tried implementing something like
Async.Parallel [quicksort lower; @ [pivot] @ quicksort upper;] |> Async.RunSynchronously
But I get syntax errors referring to the type. What am I missing here?
Upvotes: 1
Views: 188
Reputation: 243041
As mentioned by @hvester, adding parallelization to quicksort in this way will not help you much. The implementation is slow because it uses lists and allocations, not because of actual CPU bounds.
That said, if this was just an illustration to look at different ways of parallelizing F# code, then a good alternative to using Array.Parallel.map
would be to use tasks:
open System.Threading.Tasks
let rec quicksort (list: int list) =
match list with
| [] -> []
| pivot :: tail ->
let lower, upper = List.partition (fun x -> x < pivot) tail
let lowerRes = Task.Factory.StartNew(fun _ -> quicksort lower)
let upperRes = quicksort upper
lowerRes.Result @ [pivot] @ upperRes
Tasks let you start work in background using StartNew
and then wait for result by accessing the Result
property. I think this would be more appropriate in scenarios like this. Array.Parallel.map
is more intended for doing parallel processing over larger arrays.
Upvotes: 2
Reputation: 1628
Parallelizing compute-bound code, such as sorting, should be done rather with Array.Parallel.map
instead of Async.Parallel
, which is for improving throughput of IO-bound code.
You could parallelize your function as follows with Array.Parallel.map
.
let rec quicksort (list: int list) =
match list with
| [] -> [] /
| pivot :: tail ->
let lower, upper = List.partition (fun x -> x < pivot) tail
let sortedArrays = Array.Parallel.map quicksort [| lower; upper |]
sortedArrays.[0] @ [pivot] @ sortedArrays.[1]
However, you should NOT do this, because the overhead of parallelization is much higher than the benefit of parallelization and the parallelized version is actually much slower.
If you want to speed up quicksort algorithm biggest gains can be achieved by avoiding allocating objects (lists) during the algorithm. Using an array and mutating it in place is the way to go :)
Upvotes: 2