Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parallel processing in F#

Tags:

concurrency

f#

I'm playing around with async in F#. Does this look right, or am I mangling things?

let time f = 
    let before = System.DateTime.Now
    f () |> ignore
    let after = System.DateTime.Now
    after - before;;

let rec fib = function 0 | 1 -> 1
                         | n -> fib (n - 1) + fib (n - 2);;

let source = [45; 40; 45; 40]

let synchronous = time <| fun () -> List.map fib source

let para = time <| fun () -> source
                             |> List.map (fun n -> async {ignore <| fib n}) 
                             |> Async.Parallel
                             |> Async.RunSynchronously

In particular, how do I return results from an async block? Do I have to use mutable state?

Update: here's another approach:

#r "FSharp.PowerPack.Parallel.Seq.dll"
open Microsoft.FSharp.Collections

let pseq = time <| fun () -> source
                             |> PSeq.map fib
                             |> PSeq.toList
like image 985
Nick Heiner Avatar asked Jan 16 '23 07:01

Nick Heiner


1 Answers

Firstly, it's a bit of an anti-pattern to use async for parallel CPU processing. See these questions and answers for more information:

Why shouldn't I use F# asynchronous workflows for parallelism?

Task Parallel Library vs Async Workflows

Secondly, your fib function should be re-written to be tail recursive, here's an example from here (including changing to BigInt):

let fib n =
    let rec loop acc1 acc2 = function
        | n when n = 0I -> acc1
        | n -> loop acc2 (acc1 + acc2) (n - 1I)
    loop 0I 1I n

Finally, the full code:

let source = [| 45I; 40I; 45I; 40I |]

let sync = time <| fun () -> Array.map fib source

let para = time <| fun () -> Array.Parallel.map fib source

Note that in both cases an Array of the results is returned, you're just throwing it away in your time function. How about a time function that returns both the time and the result?

let time f = 
    let watch = new System.Diagnostics.Stopwatch()
    watch.Start()
    let res = f ()
    watch.Stop()
    (res, watch.ElapsedMilliseconds)

Usage remains the same, but now showing results:

printfn "Sync: %A in %ims" (fst sync) (snd sync)
printfn "Para: %A in %ims" (fst para) (snd para)
like image 68
yamen Avatar answered Jan 25 '23 21:01

yamen