F#, MailboxProcessor and Async running slow? -
background.
i trying figure out mailboxprocessor. idea use kind of state machine , pass arguments around between states , quit. parts going have async communication made sleep there. it's console application, making post nothing because main thread quits , kills behind it. making postandreply in main. also, have tried without
let sleepworkflow = async
, doesn't make difference.
questions.
(i doing wrong)
go24 not async. changing runsynchronously startimmediate makes no visible difference. end should somewhere below getme instead. @ same time done printed after fetch. isn't control supposed t returned main thread on sleep?
go24, wait go24 1, end fetch 1 done getme ...
run time terrible slow. without delay in fetch it's 10s (stopwatch). thought f# threads lightweight , should use threadpool. according debugger takes appr 1s create every , looks real threads.
also, changing [1..100] "pause" program 100s, according processexplorer 100 threads created during time , printed. prefer fewer threads , slow increase.
code.
program.fs
[<entrypoint>] let main argv = let = mailbox.messagebasedcounter.dogo24 1 let = mailbox.messagebasedcounter.dofetch 1 let b = mailbox.messagebasedcounter.getme let task = async { //mailbox.messagebasedcounter.dogo24 1 let = mailbox.messagebasedcounter.dofetch return } let stopwatch = system.diagnostics.stopwatch.startnew() let x = [1..10] |> seq.map task |> async.parallel |> async.runsynchronously stopwatch.stop() printfn "%f" stopwatch.elapsed.totalmilliseconds printfn "a: %a" printfn "b: %a" b printfn "x: %a" x 0 // return integer exit code
mailbox.fs
module mailbox #nowarn "40" type parsermsg = | go24 of int | done | fetch of int * asyncreplychannel<string> | getme of asyncreplychannel<string> type messagebasedcounter () = /// create agent static let agent = mailboxprocessor.start(fun inbox -> // message processing function let rec messageloop() = async{ let! msg = inbox.receive() match msg | go24 n -> let sleepworkflow = async{ printfn "go24, wait" do! async.sleep 4000 messagebasedcounter.dodone() // post done. printfn "go24 %d, end" n return! messageloop() } async.runsynchronously sleepworkflow | fetch (i, repl) -> let sync = async{ printfn "fetch %d" do! async.sleep 1000 repl.reply( "reply fetch " + i.tostring() ) // reply caller return! messageloop() } async.runsynchronously sync | getme (repl) -> let sync = async{ printfn "getme" repl.reply( "getme" ) // reply caller return! messageloop() } async.runsynchronously sync | done -> let sync = async{ printfn "done" return! messageloop() } async.runsynchronously sync } // start loop messageloop() ) // public interface hide implementation static member dodone () = agent.post( done ) static member dogo24 (i:int) = agent.post( go24(i) ) static member dofetch (i:int) = agent.postandreply( fun reply -> fetch(i, reply) ) static member getme = agent.postandreply( getme )
i'm not sure main problem, nested asyncs , async.runsynchrously
in agent code suspicious.
you not need create nested async - can call asynchronous operations in body of match
clauses directly:
// message processing function let rec messageloop() = async{ let! msg = inbox.receive() match msg | go24 n -> printfn "go24, wait" do! async.sleep 4000 messagebasedcounter.dodone() printfn "go24 %d, end" n return! messageloop() | fetch (i, repl) -> (...)
aside that, important understand agent has 1 instance of body computation running. so, if block body of agent, other operations queued.
if want start task (like synchronous operations) in background , resume agent immediately, can use async.start
inside body (but sure call main loop recursively in main part of body):
| go24 n -> // create work item run in background let work = async { printfn "go24, wait" do! async.sleep 4000 messagebasedcounter.dodone() printfn "go24 %d, end" n } // queue work in thread pool processed async.start(work) // continue message loop, waiting other messages return! messageloop()
Comments
Post a Comment