F# Akka.NET agents performance optimization when synchronizing -
i trying solve following problem. have agents running in real-time, large heartbeat of couple of milliseconds, , order of operations process reason deterministic (as message processing not bottleneck).
now, running large amount of simulations of system no longer have heartbeat (otherwise take couple of centuries) - need make sure order of operations preserved. this, adopted following solution: simulator makes sure each agent has processed message queue, posting dummy synchronization message , blocking while waiting answer. work application, time takes not intuitive - single threaded implementation order of magnitude faster (i guess - x 100 ish - although have not tested ).
i have isolated small test shows issue, trying use library, akka.net
type greet = | greet of string | hello of asyncreplychannel<bool> | hello2 [<entrypoint>] let main argv = let system = system.create "mysystem" <| configuration.load() let greeter = spawn system "greeter" <| fun mailbox -> let rec loop() = actor { let! msg = mailbox.receive() let sender = mailbox.sender() match msg | greet -> () // printf "hello, %s!\n" | hello2 -> sender.tell(true) | _ -> () return! loop() } loop() let greeterf = mailboxprocessor.start (fun inbox -> async { while true let! msg = inbox.receive() match msg | greet -> () // printf "hello, %s!\n" | hello reply -> reply.reply true | _ -> () } ) let n = 1000000 let t1 = system.diagnostics.stopwatch() t1.start() = 1 n let rep = greeterf.postandreply(fun reply -> (hello reply)) |> ignore () printfn "elapsed mailbox:%a" t1.elapsedmilliseconds t1.restart() = 1 n let res = greeter.ask (hello2) let rr = res.result () printfn "elapsed akka:%a" t1.elapsedmilliseconds system.console.readline () |> ignore 0
basically, both take 10 seconds mere 1 millions of synchronizations - , not computation what-so-ever involved, , is... unfortunate.
i wondering if has come across same problem , if there anyway switch off overhead forcing run in single threaded mode... better de-activating cpus 1 in bios - or writing clone of whole system without agents.
any appreciated.
the reason akka.net version beeing slow here how communicate actor:
main process task futureactorref !!threadpool!! greeter ask ----------------------> tell-----------> mailboxrun -----> (greeter mailbox empty) | <--------------------------tell <--complete task <----------.result
for each iteration, tpl task created
then single message sent greeter.
the main process blocks while waiting response come back.
the greeter replies in turn completes task inside
futureactorref
rinse , repeat.. design cause akka.net start , stop greeters "mailbox run" each message mailbox queue becomes empty each iteration. results in threadpool schedulation each single message passed.
it's bit entering car, putting pedal metal, abruptly stop , step out of car, , repeating procedure again. not effective way travel fast.
@aaronontheweb's suggestion have effect if work out above kinks in code. mailbox needs able pick messages of internal queue work messages in batches achieve full throughput.
instead, separate producer consumer. create actor listens responses greeter. , once actor have processes 1000000 messages, let actor send workcompleted message consumer.
[edit] gave shot myself, don't know f# might not idiomatic :)
open akka open akka.actor open akka.fsharp type greet = | greet of string | hello of asyncreplychannel<bool> | hello2 type consume = | response | setsender [<entrypoint>] let main argv = let system = system.create "mysystem" <| configuration.load() let greeter = spawn system "greeter" <| fun mailbox -> let rec loop() = actor { let! msg = mailbox.receive() let sender = mailbox.sender() match msg | greet -> () // printf "hello, %s!\n" | hello2 -> sender.tell(response) | _ -> () return! loop() } loop() let consumer = spawn system "consumer" <| fun mailbox -> let rec loop(count,sender : iactorref) = actor { if count = 1000000 sender.tell(true) let! msg = mailbox.receive() match msg | response -> return! loop(count+1,sender) | setsender -> return! loop(count,mailbox.sender()) } loop(0,null) let n = 1000000 let t1 = system.diagnostics.stopwatch() t1.start() = 1 n greeter.tell(hello2,consumer) let workdone = consumer.ask setsender workdone.wait() printfn "elapsed akka:%a" t1.elapsedmilliseconds system.console.readline () |> ignore 0
i updated code use separate consumer actor responses , reply once replies had been processed.
by doing so, processing time down 650ms on machine.
if want better throughput, need involve more actors parallelize more.
i'm not sure if helps in specific scenario
Comments
Post a Comment