stm-containers-0.1.0: executables/ConcurrentInsertionBench.hs
import STMContainers.Prelude
import Criterion.Main
import Control.Monad.Free
import Control.Monad.Free.TH
import qualified Data.HashMap.Strict as UC
import qualified STMContainers.Map as SC
import qualified Control.Concurrent.Async as Async
import qualified System.Random.MWC.Monad as MWC
import qualified Focus
type UCMap k v = TVar (UC.HashMap k (TVar v))
-- * Actions
-------------------------
data ActionF k v n where
Insert :: k -> v -> n -> ActionF k v n
deriving (Functor)
type Action k v = Free (ActionF k v)
-- * Interpreters
-------------------------
type Interpreter m =
forall k v r. (Hashable k, Eq k) => m k v -> Action k v r -> STM r
ucInterpreter :: Interpreter UCMap
ucInterpreter m =
iterM $ \case
Insert k v n -> do
mv <- readTVar m
vt <- newTVar v
writeTVar m $! UC.insert k vt mv
n
specializedSCInterpreter :: Interpreter SC.Map
specializedSCInterpreter m =
iterM $ \case
Insert k v n -> SC.insert v k m >> n
focusSCInterpreter :: Interpreter SC.Map
focusSCInterpreter m =
iterM $ \case
Insert k v n -> SC.focus (Focus.insertM v) k m >> n
-- * Session and runners
-------------------------
-- | A list of transactions per thread.
type Session k v = [[Action k v ()]]
type SessionRunner =
forall k v. (Hashable k, Eq k) => Session k v -> IO ()
scSessionRunner :: Interpreter SC.Map -> SessionRunner
scSessionRunner interpreter threadActions = do
m <- atomically $ SC.new
void $ flip Async.mapConcurrently threadActions $ \actions -> do
forM_ actions $ atomically . interpreter m
ucSessionRunner :: SessionRunner
ucSessionRunner threadActions = do
m <- newTVarIO UC.empty
void $ flip Async.mapConcurrently threadActions $ \actions -> do
forM_ actions $ atomically . ucInterpreter m
-- * Generators
-------------------------
type Generator a = MWC.Rand IO a
transactionGenerator :: Generator (Action Float Int ())
transactionGenerator =
Free <$> (Insert <$> k <*> v <*> n)
where
k = MWC.uniform
v = MWC.uniform
n = MWC.uniform >>= bool (return (Pure ())) transactionGenerator
-- * Utils
-------------------------
slices :: Int -> [a] -> [[a]]
slices size l =
case splitAt size l of
([], _) -> []
(a, b) -> a : slices size b
-- * Main
-------------------------
main = do
allActions <- MWC.runWithCreate $ replicateM actionsNum transactionGenerator
defaultMain $! flip map threadsNums $! \threadsNum ->
let
sliceSize = actionsNum `div` threadsNum
threadActions = slices sliceSize allActions
in
bgroup
(shows threadsNum . showString "/" . shows sliceSize $ "")
[
bgroup "STM Containers"
[
bench "Focus-based" $
scSessionRunner focusSCInterpreter threadActions,
bench "Specialized" $
scSessionRunner specializedSCInterpreter threadActions
],
bench "Unordered Containers" $
ucSessionRunner threadActions
]
where
actionsNum = 100000
threadsNums = [1, 2, 4, 8, 16, 32]