Below is a sample Memoize that never calls 'f x' more than once for the same 'x'. It does put a lot of contention around the lock if the computation-other-than-memoized-lookup is small. I show it only to demonstrate a working 'lock' example; I'm sure one could author faster code.

If you're allowed to call 'f' more than once one the same input (it will give same result, you may be wasting some CPU but perhaps can reduce lock contention) then you could choose not to lock on read and allow for failure on write (add duplicate key). The F# syntax

1
2
            try d.Add(k,v)
            with e -> ()

will ignore an exception thrown by "Add".

Anyway, take this all as some advice for 'learning F# constructs', as for 'the best way to attack this specific problem', I have to imagine it's a problem that comes up a lot and some experts have created great data structures for exactly this kind of thing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 

let TryGet (d : System.Collections.Generic.Dictionary<_,_>) key =
    let mutable v = Unchecked.defaultof<_>
    if d.TryGetValue(key, &v) then Some(v) else None

let Memoize f =
    let d = new System.Collections.Generic.Dictionary<_,_>()
    let o = new obj()
    (fun x ->
        let r = lock o (fun () -> 
            match TryGet d x with
            | None -> let r = lazy(f x) in d.Add(x, r); r
            | Some(r) -> r)
        r.Force())

let GetRate x =
    printfn "Calling GetRate(%d)..." x
    System.Threading.Thread.Sleep(2000) // expensive to compute
    printfn "Done with GetRate(%d)..." x
    100 * x

let MGetRate = Memoize GetRate    

let asyncs = Array.init 100 (fun x -> async { return MGetRate(x%9) })
let sw = System.Diagnostics.Stopwatch.StartNew()
let r = asyncs |> Async.Parallel |> Async.RunSynchronously 
printfn "Took %dms" sw.ElapsedMilliseconds 
printfn "Results: %A" r

By on 9/2/2009 10:03 AM ()

Hi James,

It seems that shared mutable state is at the heart of your async computations, which goes against the grain of functional programming. Without knowing more about the application I'd recommend trying the new concurrent collections in the Parallel Extensions library. The ConcurrentDictionary seems like a good fit.

Let us know how you get on.

Danny

By on 9/2/2009 9:39 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us | Terms of Use | Privacy Policy | Cookie Policy
Built with WebSharper