// http://zbray.com/2012/12/09/building-an-actor-in-f-with-higher-throughput-than-akka-and-erlang-actors/
open System
open System.Diagnostics
open System.Threading.Tasks
open Microsoft.FSharp.Control
let countDown = ref 0
let mre = new AutoResetEvent(false)
// Code to measure the number of messages the
// agent can process per second on a number of threads.
let test name f g =
let procCount = System.Environment.ProcessorCount
printfn "Processor count = %d." procCount
let addValue = 100L
let test2 addsPerProc : int64 =
Parallel.For(0, procCount, fun _ ->
for i = 1 to addsPerProc do f addValue)
|> ignore
let finalValue = g ()
finalValue
// Warm up!
let w = test2 1000
printfn "... warmup value: %d" w
//System.GC.Collect()
// Real test
let addsPerProc = 1000000
let stopwatch = Stopwatch.StartNew()
let finalValue = test2 addsPerProc
stopwatch.Stop()
printfn "... final value: %d" finalValue
let msgCount = procCount * addsPerProc
let expectedValue = (int64 msgCount) * addValue
if finalValue <> expectedValue then
failwith "Didn't work!"
let msgsPerSecond = int (float msgCount / stopwatch.Elapsed.TotalSeconds)
printfn "Total elapsed time = %.3f seconds." (stopwatch.Elapsed.TotalMilliseconds / 1000.0)
printfn "%s processed %d messages at the rate of %d msgs/sec"
name msgCount msgsPerSecond
printfn "Done."
type CounterMsg =
| Add of int64
| GetAndReset of (int64 -> unit)
let vanillaCounter =
MailboxProcessor.Start <| fun inbox ->
let rec loop value = async {
let! msg = inbox.Receive()
//printfn "... %d %A" value msg
match msg with
| Add v ->
return! loop (value + v)
| GetAndReset reply ->
reply value
return! loop 0L
}
loop 0L
test "Vanilla Actor (MailboxProcessor)"
(fun v -> vanillaCounter.Post (Add v))
(fun () -> vanillaCounter.PostAndReply(fun channel -> GetAndReset channel.Reply))