packages feed

ctrie-0.1.0.0: benchmarks/Concurrent.hs

{-# LANGUAGE BangPatterns #-}

module Main where

import Control.Applicative ((<$>))
import Control.Concurrent.Async
import Control.Concurrent.MVar
import Control.DeepSeq
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.IO.Class (liftIO)
import Criterion.Main
import Criterion.Config
import Data.Hashable
import Data.List (foldl')
import Data.Foldable (foldlM)
import Data.Maybe
import System.Random
import System.Random.Shuffle
import System.IO.Unsafe

import qualified Control.Concurrent.Map as CM
import qualified Data.Map as M
import qualified Data.IntMap as IM
import qualified Data.HashMap.Strict as HM

main :: IO ()
main = do
    let hmS = HM.fromList elemsS :: HM.HashMap String Int
    hmS_mvar <- newMVar hmS
    let hmS_ops241_8     = hmOps hmS_mvar elemsS 8 n      (2,4,1)
    let hmS_ops621_n10_8 = hmOps hmS_mvar elemsS 8 (n*10) (6,2,1)

    cmS <- CM.fromList elemsS :: IO (CM.Map String Int)
    let cmS_ops241_8     = cmOps cmS elemsS 8 n      (2,4,1)
    let cmS_ops621_n10_8 = cmOps cmS elemsS 8 (n*10) (6,2,1)

    putStrLn $ "n=" ++ show n

    defaultMainWith defaultConfig
        (liftIO $ do
            evaluate $ rnf hmS
            evaluate $ sum $ map length hmS_ops241_8
            evaluate $ sum $ map length hmS_ops621_n10_8
            evaluate $ rnf $ unsafePerformIO $ CM.unsafeToList cmS
            evaluate $ sum $ map length cmS_ops241_8
            evaluate $ sum $ map length cmS_ops621_n10_8
            return ()
        )
        [
            bgroup "Data.HashMap (MVar)"
            [ bgroup "String"
                [ bench "8 threads, n, 2:4:1"   $ runAll hmS_ops241_8
                , bench "8 threads, 10n, 6:2:1" $ runAll hmS_ops621_n10_8
                ]
            ]
            ,
            bgroup "Control.Concurrent.Map"
            [ bgroup "String"
                [ bench "8 threads, n, 2:4:1"   $ runAll cmS_ops241_8
                , bench "8 threads, 10n, 6:2:1" $ runAll cmS_ops621_n10_8
                ]
            ]
        ]
    where
        n = 2^12
        elemsS = zip keysS [1..n]
        elemsI = zip keysI [1..n]
        keysS = rndS 8 n
        keysI = rndI (n+n) n


rndS :: Int -> Int -> [String]
rndS strlen num = take num $ split $ randomRs ('a', 'z') $ mkStdGen 1234
    where
        split cs = case splitAt strlen cs of (str, cs') -> str : split cs'

rndI :: Int -> Int -> [Int]
rndI upper num = take num $ randomRs (0, upper) $ mkStdGen 1234


runAll :: [[IO ()]] -> IO ()
runAll ops = do
    as <- mapM (async . sequence_) ops
    mapM_ wait as


mkOps :: (k -> m -> IO ())       -- lookup function
      -> (k -> v -> m -> IO ())  -- insert function
      -> (k -> m -> IO ())       -- delete function
      -> m -> [(k,v)]            -- the map & the elements from which to draw
      -> Int                     -- the number of threads
      -> Int                     -- the number of operations per thread
      -> (Int, Int, Int)         -- ratio of lookups : insertions : deletions
      -> [[IO ()]]
mkOps lookup insert delete m elems nThreads nOps (rl,ri,rd) =
    let tot = fromIntegral $ rl + ri + rd
        numLookups = ceiling $ fromIntegral nOps * (fromIntegral rl / tot)
        numInserts = ceiling $ fromIntegral nOps * (fromIntegral ri / tot)
        numDeletes = ceiling $ fromIntegral nOps * (fromIntegral rd / tot)

        lookupElems = shuffle' (take nOps $ cycle elems) nOps (mkStdGen 1234)
        insertElems = shuffle' (take nOps $ cycle elems) nOps (mkStdGen 5678)
        deleteElems = shuffle' (take nOps $ cycle elems) nOps (mkStdGen 9012)

        ops0 = [lookup k   m | (k,_) <- take numLookups lookupElems]
            ++ [insert k v m | (k,v) <- take numInserts insertElems]
            ++ [delete k   m | (k,_) <- take numDeletes deleteElems]

        ops = take nThreads
            $ iterate (\ops -> shuffle' ops nOps (mkStdGen 1234))
            $ take nOps ops0
    in ops

-----------------------------------------------------------------------
-- Control.Concurrent.Map

cmOps = mkOps cm_lookup CM.insert CM.delete

cm_lookup :: (Eq k, Hashable k) => k -> CM.Map k v -> IO ()
cm_lookup k m = do
    let v = CM.lookup k m
    v `seq` return ()
{-# SPECIALIZE cm_lookup :: String -> CM.Map String Int -> IO () #-}
{-# SPECIALIZE cm_lookup :: Int -> CM.Map Int Int -> IO () #-}


-----------------------------------------------------------------------
-- Data.HashMap

hmOps = mkOps hm_lookup hm_insert hm_delete

hm_lookup :: (Eq k, Hashable k) => k -> MVar (HM.HashMap k v) -> IO ()
hm_lookup k mvar = do
    --return undefined
    m <- takeMVar mvar
    putMVar mvar m
    let v = HM.lookup k m
    v `seq` return ()
{-# SPECIALIZE hm_lookup :: String -> MVar (HM.HashMap String Int) -> IO () #-}
{-# SPECIALIZE hm_lookup :: Int -> MVar (HM.HashMap Int Int) -> IO () #-}

hm_insert :: (Eq k, Hashable k) => k -> v -> MVar (HM.HashMap k v) -> IO ()
hm_insert k v mvar = do
    --return ()
    m <- takeMVar mvar
    putMVar mvar $! HM.insert k v m
{-# SPECIALIZE hm_insert :: String -> Int -> MVar (HM.HashMap String Int) -> IO () #-}
{-# SPECIALIZE hm_insert :: Int -> Int -> MVar (HM.HashMap Int Int) -> IO () #-}

hm_delete :: (Eq k, Hashable k) => k -> MVar (HM.HashMap k v) -> IO ()
hm_delete k mvar = do
    --return ()
    m <- takeMVar mvar
    putMVar mvar $! HM.delete k m
{-# SPECIALIZE hm_delete :: String -> MVar (HM.HashMap String Int) -> IO () #-}
{-# SPECIALIZE hm_delete :: Int -> MVar (HM.HashMap Int Int) -> IO () #-}