packages feed

stm-hamt-1: concurrent-insertion-bench/Main.hs

module Main where

import Rebase.Prelude
import Criterion.Main
import Control.Monad.Free
import Control.Monad.Free.TH
import qualified StmHamt.Hamt as A
import qualified Control.Concurrent.Async as B
import qualified System.Random.MWC.Monad as C
import qualified Focus as D
import qualified Rebase.Data.Text as E
import qualified Rebase.Data.Vector as F


-- * Transactions 
-------------------------

data TransactionF row n where
  Insert :: row -> n -> TransactionF row n
  deriving (Functor)

type Transaction row = Free (TransactionF row)


-- * Interpreters
-------------------------

type Interpreter container = 
  forall row. (Hashable row, Eq row) => container row -> forall result. Transaction row result -> STM result

specializedInterpreter :: Interpreter A.Hamt
specializedInterpreter container =
  iterM $ \case
    Insert row continue -> A.insert id row container >> continue

focusInterpreter :: Interpreter A.Hamt
focusInterpreter container =
  iterM $ \case
    Insert row continue -> A.focus (D.insert row) id row container >> continue


-- * Session and runners
-------------------------

-- | A list of transactions per thread.
type Session row = [[Transaction row ()]]

type SessionRunner = 
  forall row. (Hashable row, Eq row) => Session row -> IO ()

sessionRunner :: Interpreter A.Hamt -> SessionRunner
sessionRunner interpreter threadTransactions = do
  m <- atomically $ A.new
  void $ flip B.mapConcurrently threadTransactions $ \actions -> do
    forM_ actions $ atomically . interpreter m


-- * Generators
-------------------------

type Generator a = C.Rand IO a

transactionGenerator :: Generator (Transaction Text ())
transactionGenerator = do
  text <- textGenerator
  return $ Free $ Insert text (Pure ())

textGenerator :: Generator Text
textGenerator = do
  l <- length
  s <- replicateM l char
  return $! E.pack s
  where
    length =
      C.uniformR (7, 20)
    char =
      chr <$> C.uniformR (ord 'a', ord 'z')


-- * Utils
-------------------------

slices :: Int -> [a] -> [[a]]
slices size l =
  case splitAt size l of
    ([], _) -> []
    (a, b) -> a : slices size b


-- * Main
-------------------------

main = do
  allTransactions <- C.runWithSeed seed $ replicateM actionsNum transactionGenerator
  defaultMain $! flip map threadsNums $! \threadsNum ->
    let
      sliceSize = actionsNum `div` threadsNum
      threadTransactions = slices sliceSize allTransactions
      in 
        bgroup
          (shows threadsNum . showString "/" . shows sliceSize $ "")
          [
            bench "Focus-based" $ nfIO $
              sessionRunner focusInterpreter threadTransactions,
            bench "Specialized" $ nfIO $
              sessionRunner specializedInterpreter threadTransactions
          ]
  where
    seed =
      C.toSeed (F.fromList [1..7])
    actionsNum =
      100000
    threadsNums =
      [1, 2, 4, 6, 8, 12, 16, 32, 40, 52, 64, 80, 128]