I was testing early-returning in F# async and noticed some strange memory usage behaviour with MailboxProcessor. Consider this program:
let mp = MailboxProcessor<bool>.Start (fun inbox ->
let mutable count = 0
let rec await () = async {
let! msg = inbox.Receive ()
count <- count + 1
if msg then
printfn "Count: %4i | Queued: %4i" count inbox.CurrentQueueLength
return! await ()
else return! finish () }
and finish () = async {
printfn "done" }
await ())
let numPosts = 10_000
for _ = 1 to numPosts do
mp.Post true
System.Threading.Thread.Sleep 1
mp.Post false
System.Console.ReadLine () |> ignore
If you delete the else, the memory usage keeps growing, which I expected, because the return! finish ()s keep piling up on the stack, as you can see with all of the dones which are printed upon completion. But if you put the else back in, thus making await tail-recursive, the memory usage still grows apparently indefinitely, although not as quickly, while the queue length remains at zero.
If you delete the Sleep line and raise numPosts to 100_000, the memory usage rises quickly with queue length but then stays more or less steady while the messages are processed, even remaining high after completion. This surprises me a little as well, as I would have expected the memory usage to decline with queue length.
Can anyone explain these memory usage behaviours?
System.GC.Collect(). This will slow down the performance, but give you a better picture of memory usage.