4

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
Nick Heiner
  • 119,074
  • 188
  • 476
  • 699

1 Answers1

9

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)
Community
  • 1
  • 1
yamen
  • 15,390
  • 3
  • 42
  • 52
  • Yeah, I was just throwing away the result because I didn't care about the computation; just how long it took. What about using PSeq from the F# Power Pack? – Nick Heiner Jun 24 '12 at 22:28
  • Sure, you could use that if you had anything more complex then `map` to do. As it stands, what we've got here is the most efficient for your particular problem. Further reading here: http://stackoverflow.com/questions/4851745/does-f-async-parallel-speed-up-calculations (second answer by Tomas, and the linked blog also). – yamen Jun 24 '12 at 23:11