Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F#: Tell me what I'm missing about using Async.Parallel

ok, so I'm doing ProjectEuler Problem #14, and I'm fiddling around with optimizations in order to feel f# out.

in the following code:

let evenrule n = n / 2L
let oddrule n = 3L * n + 1L

let applyRule n =
    if n % 2L = 0L then evenrule n
    else oddrule n

let runRules n =
    let rec loop a final =
        if a = 1L then final
        else loop (applyRule a) (final + 1L)
    n, loop (int64 n) 1L


let testlist = seq {for i in 3 .. 2 .. 1000000 do yield i } 

let getAns sq = sq |> Seq.head

let seqfil (a,acc) (b,curr) = if acc = curr then (a,acc) else if acc < curr then (b,curr) else (a,acc)

let pmap f l = 
    seq { for a in l do yield async {return f a} }
    |> Seq.map Async.RunSynchronously

let pmap2 f l = 
    seq { for a in l do yield async {return f a} }
    |> Async.Parallel
    |> Async.RunSynchronously

let procseq f l = l
                  |> f runRules
                  |> Seq.reduce seqfil
                  |> fst

let timer = System.Diagnostics.Stopwatch()
timer.Start()
let ans1 = testlist |> procseq Seq.map // 837799    00:00:08.6251990
printfn "%A\t%A" ans1 timer.Elapsed
timer.Reset()

timer.Start()
let ans2 = testlist |> procseq pmap
printfn "%A\t%A" ans2 timer.Elapsed // 837799   00:00:12.3010250
timer.Reset()

timer.Start()
let ans3 = testlist |> procseq pmap2
printfn "%A\t%A" ans3 timer.Elapsed // 837799   00:00:58.2413990
timer.Reset()

Why does the Async.Parallel code run REALLY slow in comparison to the straight up map? I know I shouldn't see that much of an effect, since I'm only on a dual core mac.

Please note that I do NOT want help solving problem #14, I just want to know what's up with my parallel code.

like image 520
Jon Bristow Avatar asked Oct 17 '25 18:10

Jon Bristow


1 Answers

The use of Async.Parallel seems to be correct. The numbers really look suspicious, but I don't immediately see what may be a problem here.

In any case, asynchronous workflows are really more suitable for computations that involve some asynchronous operation (such as I/O, communication, waiting for events, etc.). For CPU intensive tasks, it is better to use Parallel Extensions to .NET (which are now a part of .NET 4.0; unfortunatelly there is no .NET 2.0 version).

To do that from F#, you'll need F# PowerPack and the FSharp.PowerPack.Parallel.Seq.dll assembly, which contains parallel versions of higher-order functions for working with sequences (such as map :-))

These functions return a value of type pseq<'a> (called ParallelQuery<T> in C#), which represent a delayed computation running in parallel (this enables better optimizations when you use multiple operations in pipeline). There is also PSeq.reduce function, so you may want to use this in your processing too (aside from PSeq.map).

like image 126
Tomas Petricek Avatar answered Oct 20 '25 09:10

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!