STM: F# vs Haskell

STM is a very nice parallel programming model used intensively in Haskell and Clojure. There's a F# implementation which can be found in FSharpx library.

Today I'm going to test performance of both the Haskell and the F# STMs. The test is very simple - read a couple TVars, check their equality, then write them back incremented by 1, repeat a million times.

First, the Haskell code:

import Control.Concurrent.STM
import Control.Monad (forM_)
import System.Environment (getArgs)
main :: IO ()
main = do
nStr:_ <- getArgs
let n = read nStr :: Int
x <- atomically $ newTVar (0::Int)
y <- atomically $ newTVar (0::Int)
forM_ [1..n] $ atomically . \_ -> do
x' <- readTVar x
y' <- readTVar y
check (x' == y')
writeTVar x (x' + 1)
writeTVar y (y' + 1)
print "Done."
{-
STMTest 1000000 +RTS -s
"Done."
16,054,596 bytes allocated in the heap
5,264 bytes copied during GC
41,860 bytes maximum residency (2 sample(s))
23,676 bytes maximum slop
1 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 29 colls, 0 par 0.00s 0.00s 0.0000s 0.0000s
Gen 1 2 colls, 0 par 0.00s 0.00s 0.0001s 0.0001s
INIT time 0.00s ( 0.00s elapsed)
MUT time 0.17s ( 0.17s elapsed)
GC time 0.00s ( 0.00s elapsed)
EXIT time 0.00s ( 0.00s elapsed)
Total time 0.17s ( 0.17s elapsed)
%GC time 0.0% (0.3% elapsed)
Alloc rate 93,557,652 bytes per MUT second
Productivity 100.0% of total user, 100.9% of total elapsed -}
view raw stm.hs hosted with ❤ by GitHub

So, it took about 170 ms. OK, now F#:

open FSharpx.Stm
open FSharpx.TimeMeasurement
[<EntryPoint>]
let main argv =
let n = int (argv.[0])
let x = newTVar 0
let y = newTVar 0
// FSharpx's STM does not define the Haskell's check function, so we define our own here
let check (b: bool) = stm { if b then return () else return! retry() }
stopTime <| fun() ->
seq { 1..n }
|> mapM_ (fun i ->
stm {
let! x' = readTVar x
let! y' = readTVar y
do! check (x' = y')
do! writeTVar x (x' + 1)
do! writeTVar y (y' + 1) })
|> atomically
|> snd
|> printfn "Elapsed: %A ms"
0
(*
> FSharpSTMTest.exe 1000000
Elapsed: 1572.0 ms
*)
view raw stm.fs hosted with ❤ by GitHub
It took about 1,6 seconds which is an order of magnitude slower than the Haskell result. It's rather frustrating.

Comments

Unknown said…
Was the F# test done on Mono or native windows?
KolA said…
Did you eliminate JIT compile overhead in F# ?
Unknown said…
Repeat this test on .Net Core 2

Popular posts from this blog

Regular expressions: Rust vs F# vs Scala

Hash maps: Rust, F#, D, Go, Scala

Haskell: performance