Skip to content

Instantly share code, notes, and snippets.

@lefthandedgoat
Created June 2, 2014 15:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lefthandedgoat/a5934130cad7f293a2c5 to your computer and use it in GitHub Desktop.
Save lefthandedgoat/a5934130cad7f293a2c5 to your computer and use it in GitHub Desktop.
open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Diagnostics
open System
let fetch name (url:string) =
printfn "fetching %s" name
let uri = new System.Uri(url)
use webClient = new WebClient()
let stopwatch = Stopwatch()
stopwatch.Start()
let html = webClient.DownloadString(uri)
decimal stopwatch.Elapsed.Milliseconds
let random = Random()
let sleep fromMS toMS accumulator =
let milliseconds = random.Next(fromMS, toMS)
System.Threading.Thread.Sleep(milliseconds)
accumulator
let start = 0.0M
let microsoft accumulator = accumulator + fetch "Microsoft.com" "http://www.microsoft.com/en-us/default.aspx"
let msdn accumulator = accumulator + fetch "MSDN" "http://msdn.microsoft.com/"
let bing accumulator = accumulator + fetch "Bing" "http://www.bing.com"
let duckduckgo accumulator = accumulator + fetch "Duck Duck Go" "https://duckduckgo.com/"
let yahoo accumulator = accumulator + fetch "Yahoo" "http://www.yahoo.com"
let google accumulator = accumulator + fetch "Google" "http://www.google.com"
let generate number func = [ for x in 1 .. number do yield func]
let normalWorkflow () =
start
|> microsoft
|> sleep 1000 2000
|> msdn
|> sleep 1500 2000
|> bing
|> sleep 2000 3000
|> duckduckgo
|> sleep 1000 2000
|> yahoo
|> sleep 4000 6000
|> google
let fastWorkflow () =
start
|> microsoft
|> sleep 100 200
|> msdn
|> sleep 150 200
|> bing
|> sleep 200 300
|> duckduckgo
|> sleep 100 200
|> yahoo
|> sleep 400 600
|> google
let all =
generate 5 normalWorkflow
@ generate 10 fastWorkflow
@ generate 50 normalWorkflow
@ generate 2 fastWorkflow
@ generate 4 normalWorkflow
let results =
all
|> Array.ofList
|> Array.Parallel.map (fun f -> f())
@lefthandedgoat
Copy link
Author

open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Diagnostics
open System

let fetch name (url:string) =    
    printfn "fetching %s" name
    let uri = new System.Uri(url)
    use webClient = new WebClient()
    let stopwatch = Stopwatch()
    stopwatch.Start()
    let html = webClient.DownloadString(uri)
    stopwatch.Stop()
    decimal stopwatch.Elapsed.Milliseconds

let random = Random()

let sleep fromMS toMS accumulator =     
    let milliseconds = random.Next(fromMS, toMS)
    System.Threading.Thread.Sleep(0)
    accumulator

let microsoft accumulator = accumulator + fetch "Microsoft.com" "http://localhost:52101"
let msdn accumulator = accumulator + fetch "MSDN" "http://localhost:52101"
let bing accumulator = accumulator + fetch "Bing" "http://localhost:52101"
let duckduckgo accumulator = accumulator + fetch "Duck Duck Go" "http://localhost:52101"
let yahoo accumulator = accumulator + fetch "Yahoo" "http://localhost:52101"
let google accumulator = accumulator + fetch "Google" "http://localhost:52101"

type 'a job =   
    | Die   
    | Goto of (decimal -> decimal)
    | Sleep of int * int
    | Next of AsyncReplyChannel<'a>   

let worker ()  =   
    MailboxProcessor.Start(fun inbox ->
        let rec loop =   
            async { let! msg = inbox.Receive()   
                    match msg with   
                    | Die -> return ()   
                    | Sleep(from, too) -> 
                        //printfn "sleeping"
                        let milliseconds = random.Next(from, too)
                        do! Async.Sleep(milliseconds)
                        return! loop
                    | Goto(f) -> 
                        //printfn "going"
                        f 0.0M |> ignore
                        return! loop
                    | Next(reply) ->   
                        reply.Reply()   
                        return! loop }   
        loop)  

let workers =
    [ 1 .. 5000 ]
    |> List.map (fun i -> worker())

workers
|> List.iteri (fun c w -> 
        w.Post(Sleep(100,200))
        w.Post(Goto(microsoft))
        w.Post(Sleep(1000,4000))        
        w.Post(Goto(msdn))
        w.Post(Sleep(1005,2000))
        w.Post(Goto(bing))
        w.Post(Sleep(2000,3000))
        w.Post(Goto(duckduckgo))
        w.Post(Sleep(1000,2000))
        w.Post(Goto(yahoo))
        w.Post(Sleep(400,600))
        w.Post(Goto(google))
    )

workers
|> List.iteri (fun c w -> w.Post(Die))

@lefthandedgoat
Copy link
Author

much better with worker and manager and accountant that tells the manager how many more workes to employ

open System.Net
open Microsoft.FSharp.Control.WebExtensions
open System.Diagnostics
open System

let fetch name (url:string) =    
    printfn "fetching %s" name
    let uri = new System.Uri(url)
    use webClient = new WebClient()
    let stopwatch = Stopwatch()
    stopwatch.Start()
    let html = webClient.DownloadString(uri)
    stopwatch.Stop()
    stopwatch.Elapsed.Milliseconds

let random = Random()

let microsoft () = fetch "Microsoft.com" "http://localhost:52101"
let msdn () = fetch "MSDN" "http://localhost:52101"
let bing () = fetch "Bing" "http://localhost:52101"
let duckduckgo () = fetch "Duck Duck Go" "http://localhost:52101"
let yahoo () = fetch "Yahoo" "http://localhost:52101"
let google () = fetch "Google" "http://localhost:52101"

type work =   
    | Die   
    | Goto of (unit -> int)
    | Sleep of int * int
    | Done

type report =
    | Stats of int
    | Instructions of AsyncReplyChannel<int>
    | Die    

type worker = { id : int; worker : MailboxProcessor<work> }

type manage =
    | Enlist of worker
    | Go
    | Done of int
    | Die

type result = { milliseconds : int }

let doWork (w : MailboxProcessor<work>) =
    w.Post(Sleep(100,200))
    w.Post(Goto(microsoft))
    w.Post(Sleep(1000,4000))
    w.Post(Goto(msdn))
    w.Post(Sleep(1005,2000))
    w.Post(Goto(bing))
    w.Post(Sleep(2000,3000))
    w.Post(Goto(duckduckgo))
    w.Post(Sleep(1000,2000))
    w.Post(Goto(yahoo))
    w.Post(Sleep(400,600))
    w.Post(Goto(google))
    w.Post(work.Done)

let accountant () =
    MailboxProcessor.Start(fun inbox ->
        let rec loop nums =   
            async { let! msg = inbox.Receive()   
                    match msg with   
                    | report.Die -> return ()
                    | Instructions(reply) -> 
                        if List.isEmpty nums then
                            reply.Reply(0)
                            return! loop []
                        else
                            let head = List.head nums
                            reply.Reply(head)
                            return! loop <| List.tail nums
                    | Stats(ms) -> 
                        printfn "done in: %i" ms
                        return! loop nums }   
        loop [1 .. 50])

let manager (accountant : MailboxProcessor<report>) =
    MailboxProcessor.Start(fun inbox ->
        let rec loop (workers : worker list) (busyWorkers : worker list) =   
            async { let! msg = inbox.Receive()   
                    match msg with   
                    | manage.Die -> return ()
                    | manage.Go ->
                        let worker = List.head workers
                        doWork worker.worker
                        return! loop (List.tail workers) ([worker] @ busyWorkers)
                    | manage.Enlist(worker) -> 
                        let workers = [worker] @ workers
                        return! loop workers busyWorkers
                    | manage.Done(id) -> 
                        let! numberOfNeededWorkers = accountant.PostAndAsyncReply(Instructions) 
                        printfn "adding %i workers" numberOfNeededWorkers                        
                        let worker = busyWorkers |> List.find (fun w -> w.id = id)
                        let workers = [worker] @ workers
                        let aboutToBeBusy = workers |> Seq.take numberOfNeededWorkers |> List.ofSeq                        

                        aboutToBeBusy |> List.iter (fun w -> doWork w.worker)
                        let busyWorkers = aboutToBeBusy @ busyWorkers
                        let workers = 
                            workers
                            |> Seq.skip numberOfNeededWorkers
                            |> List.ofSeq
                        return! loop workers busyWorkers }
        loop [] [])

let worker (id : int) (accountant : MailboxProcessor<report>) (manager : MailboxProcessor<manage>) =   
    MailboxProcessor.Start(fun inbox ->
        let rec loop ms =   
            async { let! msg = inbox.Receive()   
                    match msg with   
                    | work.Die -> return ()
                    | Sleep(from, too) ->
                        let milliseconds = random.Next(from, too)
                        do! Async.Sleep(milliseconds)
                        return! loop ms
                    | Goto(func) ->
                        return! loop <| func () + ms 
                    | work.Done ->
                        accountant.Post(Stats(ms))
                        manager.Post(Done(id))
                        return! loop 0 }
        loop 0)  

let accnt = accountant ()
let mgr = manager accnt

let workers =
    [ 1 .. 5000 ]
    |> List.map (fun id -> { id = id; worker = worker id accnt mgr })

workers |> List.iter (fun w -> mgr.Post(Enlist(w)))  

mgr.Post(Go)

System.Console.ReadKey()

workers |> List.iter (fun w -> w.worker.Post(work.Die))
accnt.Post(report.Die)
mgr.Post(manage.Die)

System.Console.ReadKey()

@panesofglass
Copy link

Why not just edit the gist? You can always see the previous edits in the revisions. This looks really cool, by the way.

@lefthandedgoat
Copy link
Author

Sorry didn't see the response. I will start editing the gist ;p

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment