packages feed

ttrie-0.1.0.0: benchmarks/Common.hs

{-# LANGUAGE DeriveGeneric #-}
module Common where

import BenchGen

import Control.Applicative
import Control.Concurrent.Async
import Control.Concurrent.STM
import Control.Concurrent.STM.Stats
import Control.DeepSeq
import Control.Monad
import Criterion.Main
import qualified Data.Map as Map
import Data.Text (Text)
import qualified Data.Text as T
import GHC.Generics
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

------------------------------------------------------------------------------

data Op = Lookup | Insert | Delete
    deriving (Eq, Show, Generic)

instance NFData Op

key :: Generator IO k Text
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)

------------------------------------------------------------------------------

runAll :: [Int] -> Int -> CondensedTableV Int -> Config IO Op Text -> IO ()
runAll numThreads numTransactions sizes config = do
    txs <- genTransactions numTransactions sizes config
    runBenchmarks txs numTransactions numThreads
    collectRetryStats txs numTransactions numThreads

genTransactions :: Int -> CondensedTableV Int -> Config IO Op Text -> IO [[(Op, Text)]]
genTransactions numTransactions sizes config = do
    printf "Generating %d random transactions...\n" numTransactions
    gen <- create
    runGenerator gen $ do
        replicateM numTransactions $ do
            size <- liftGen $ genFromTable sizes
            generateOperations config size

runBenchmarks :: [[(Op, Text)]] -> Int -> [Int] -> IO ()
runBenchmarks txs numTransactions numThreads =
    defaultMain $! flip map numThreads
         $ \n -> env (return $! split (numTransactions `div` n) txs)
         $ \ ~ops -> bgroup (printf "%d/%d" n (numTransactions `div` n))
         [ bench "unordered-containers" $ whnfIO $ run ops hashmapEval =<< newTVarIO HashMap.empty
         , bench "unordered-containers2" $ whnfIO $ run ops hashmapEval2 =<< newTVarIO HashMap.empty
         , bench "stm-containers"       $ whnfIO $ run ops stmcontEval =<< STMCont.newIO
         , bench "ttrie"                $ whnfIO $ run ops ttrieEval =<< atomically TTrie.empty
         ]
  where
    run ops f c = mapConcurrently (mapM_ txEval) ops
      where txEval = atomically . mapM_ (\(op,k) -> f op k c)

collectRetryStats :: [[(Op, Text)]] -> Int -> [Int] -> IO ()
collectRetryStats txs numTransactions numThreads =
    flip mapM_ numThreads $ \n -> do
        let ops = split (numTransactions `div` n) txs
            str = printf "%d/%d" n (numTransactions `div` n)
        run (str++"/unordered-containers") ops hashmapEval =<< newTVarIO HashMap.empty
        run (str++"/unordered-containers2") ops hashmapEval2 =<< newTVarIO HashMap.empty
        run (str++"/stm-containers")       ops stmcontEval =<< STMCont.newIO
        run (str++"/ttrie")                ops ttrieEval =<< atomically TTrie.empty
  where
    run name ops f c = do
        printf "collecting retry statistics for %s\n" name
        mapConcurrently (mapM_ txEval) ops
        printStats name
      where
        txEval = atomically' name . mapM_ (\(op,k) -> f op k c)
        atomically' = trackSTMConf defaultTrackSTMConf
                            { tryThreshold = Nothing
                            , globalTheshold = Nothing
                            }
        printStats name = do
            stats <- getSTMStats
            let Just (commits, retries) = Map.lookup name stats
            printf "Commits\t%d\nRetries\t%d\n\n" commits retries

split n [] = []
split n xs = let (ys,zs) = splitAt n xs in ys : split n zs

------------------------------------------------------------------------------
-- Evaluation functions

ttrieEval Lookup k = void . TTrie.lookup k
ttrieEval Insert k = TTrie.insert k ()
ttrieEval Delete k = TTrie.delete k

stmcontEval Lookup k = void . STMCont.lookup k
stmcontEval Insert k = STMCont.insert () k
stmcontEval Delete k = STMCont.delete k

hashmapEval Lookup k m = do
    v <- HashMap.lookup k <$> readTVar m
    case v of
        Nothing -> return ()
        Just v -> readTVar v >> return ()
hashmapEval Insert k m = do
    v <- newTVar ()
    modifyTVar' m (HashMap.insert k v)
hashmapEval Delete k m = modifyTVar' m (HashMap.delete k)

hashmapEval2 Lookup k m = HashMap.lookup k <$> readTVar m >> return ()
hashmapEval2 Insert k m = modifyTVar' m (HashMap.insert k ())
hashmapEval2 Delete k m = modifyTVar' m (HashMap.delete k)