ttrie-0.1.2.1: benchmarks/Bench.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Main where
import BenchGen
import Control.Applicative
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Concurrent.STM.Stats
import Control.DeepSeq
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.IO.Class
import CriterionPlus
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics
import GHC.Stats
import System.Environment
import System.Mem
import System.Random.MWC
import System.Random.MWC.CondensedTable
import Text.Printf
import qualified Control.Concurrent.STM.Map as TTrie
import qualified STMContainers.Map as STMCont
import qualified Data.HashMap.Strict as HashMap
------------------------------------------------------------------------------
main :: IO ()
main = do
(arg1:arg2:arg3:arg4:arg5:args) <- getArgs
printf "threads = %s\n" arg1
printf "numTransactions = %s\n" arg2
printf "sizes = %s\n" arg3
printf "numPrefill = %s\n" arg4
printf "ops = %s\n" arg5
let threads = read arg1 :: [Int]
numTransactions = read arg2
sizes = mkTable $ read arg3
numPrefill = read arg4
(ins,upd,look,del) = read arg5
config = Config
{ operations = mkTable [ (Insert, ins + upd)
, (Lookup, look)
, (Delete, del)
]
, keys = \op -> case op of
Insert -> mkTable [(remember key, ins), (remember (reuse key), upd)]
Lookup -> mkTable [(reuse key, 1)]
Delete -> mkTable [(forget (reuse key), 1)]
}
(ks,txs) <- genTransactions numPrefill numTransactions sizes config
withArgs args $ benchmark
$ standoff (T.pack arg5)
$ mapM_ (supergroup ks txs numTransactions)
$ threads
supergroup :: [Key] -> [Transaction] -> Int -> Int -> Standoff ()
supergroup ks txs numTransactions t =
group (T.pack $ printf "%d/%d" t n) $ do
runAll "unordered-containers" hashmapPrefill hashmapEval
runAll "stm-containers" stmcontPrefill stmcontEval
runAll "ttrie" ttriePrefill ttrieEval
where
n = numTransactions `div` t
ops = split n txs
runAll name prefill eval = do
runTime name prefill eval ks ops
liftIO $ runRetries (printf "%d/%d/%s" t n name) prefill eval ks ops
liftIO $ runAlloc prefill eval ks ops
split :: Int -> [a] -> [[a]]
split n [] = []
split n xs = let (ys,zs) = splitAt n xs in ys : split n zs
------------------------------------------------------------------------------
type EvalFunc c = Op -> Key -> c -> STM ()
type PrefillFunc c = [Key] -> IO c
runTime :: String -> PrefillFunc c -> EvalFunc c -> [Key] -> [[Transaction]] -> Standoff ()
runTime name prefill eval ks ops = subject (T.pack name) $ do
pause
c <- liftIO $ prefill ks
continue
liftIO $ run_ atomically ops eval c
runRetries :: String -> PrefillFunc c -> EvalFunc c -> [Key] -> [[Transaction]] -> IO ()
runRetries name prefill eval ks ops = do
printf "commits: "
c <- prefill ks
let atomically' = trackSTMConf defaultTrackSTMConf
{ tryThreshold = Nothing
, globalTheshold = Nothing
}
run_ (atomically' name) ops eval c
stats <- getSTMStats
let Just (commits, retries) = Map.lookup name stats
printf "%d\nretries: %d\n" commits retries
runAlloc :: PrefillFunc c -> EvalFunc c -> [Key] -> [[Transaction]] -> IO ()
runAlloc prefill eval ks ops = do
printf "allocated bytes: "
c <- prefill ks
performGC
before <- getGCStats
run_ atomically ops eval c
performGC
after <- getGCStats
let bytes = bytesAllocated after - bytesAllocated before
printf "%d\n" bytes
run_ :: (STM () -> IO ()) -> [[Transaction]] -> EvalFunc c -> c -> IO ()
run_ atom ops f c = void $ mapConcurrently (mapM_ txEval) ops
where txEval = atom . mapM_ (\(op,k) -> f op k c)
------------------------------------------------------------------------------
type Key = Text
type Transaction = [(Op,Key)]
data Op = Lookup | Insert | Delete
deriving (Eq, Show, Generic)
instance NFData Op
key :: Generator IO k Key
key = do
n <- liftGen $ genFromTable length
T.pack <$> replicateM n (liftGen $ genFromTable alphabet)
where
length = mkTable $ zip [7..20] (repeat 1)
alphabet = mkTable $ zip ['a'..'z'] (repeat 1)
genTransactions :: Int -> Int -> CondensedTableV Int -> Config IO Op Key -> IO ([Key],[Transaction])
genTransactions numPrefill numTransactions sizes config = do
gen <- create
runGenerator gen $ do
liftIO $ printf "Generating %d random keys to prefill...\n" numPrefill
ks <- replicateM numPrefill (remember key)
liftIO $ printf "Generating %d random transactions...\n" numTransactions
txs <- replicateM numTransactions $ do
size <- liftGen $ genFromTable sizes
generateOperations config size
return (ks,txs)
------------------------------------------------------------------------------
-- Evaluation and Prefill functions
instance (NFData k, NFData v) => NFData (TTrie.Map k v)
ttriePrefill :: PrefillFunc (TTrie.Map Key ())
ttriePrefill ks = do
m <- atomically $ TTrie.empty
forM_ ks $ \k -> atomically $ TTrie.insert k () m
evaluate (rnf m)
return m
ttrieEval :: EvalFunc (TTrie.Map Key ())
ttrieEval Lookup k = void . TTrie.lookup k
ttrieEval Insert k = TTrie.insert k ()
ttrieEval Delete k = TTrie.delete k
instance (NFData k, NFData v) => NFData (STMCont.Map k v)
stmcontPrefill :: PrefillFunc (STMCont.Map Key ())
stmcontPrefill ks = do
m <- atomically $ STMCont.new
forM_ ks $ \k -> atomically $ STMCont.insert () k m
evaluate (rnf m)
return m
stmcontEval :: EvalFunc (STMCont.Map Key ())
stmcontEval Lookup k = void . STMCont.lookup k
stmcontEval Insert k = STMCont.insert () k
stmcontEval Delete k = STMCont.delete k
instance (NFData a) => NFData (TVar a)
hashmapPrefill :: PrefillFunc (TVar (HashMap.HashMap Key ()))
hashmapPrefill ks = do
let m = HashMap.fromList $ zip ks $ repeat ()
evaluate (rnf m)
newTVarIO m
hashmapEval :: EvalFunc (TVar (HashMap.HashMap Key ()))
hashmapEval Lookup k m = void $ HashMap.lookup k <$> readTVar m
hashmapEval Insert k m = modifyTVar' m (HashMap.insert k ())
hashmapEval Delete k m = modifyTVar' m (HashMap.delete k)