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
asked Jun 24, 2012 at 20:53
3
  • Maybe this is a fit for codereview.stackexchange.com? Commented Jun 24, 2012 at 21:13
  • ildjarn what about let foo = async {3 + 4}. It gives warning FS0020: 3 + 4 should have type unit. Commented Jun 24, 2012 at 21:25
  • 2
    @Rosarch : let foo = async { return (3 + 4) } Commented Jun 24, 2012 at 21:27

1 Answer 1

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)
answered Jun 24, 2012 at 21:25
Sign up to request clarification or add additional context in comments.

2 Comments

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?
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: stackoverflow.com/questions/4851745/… (second answer by Tomas, and the linked blog also).

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.