packages feed

kazura-queue-0.1.0.3: bench/Main.hs

{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types                #-}

module Main where

import qualified Control.Concurrent.KazuraQueue as KZR

-- import qualified Control.Concurrent.Chan.Unagi as UNG

import qualified Control.Concurrent      as CC
import qualified Control.Concurrent.Chan as Chan
import qualified Control.Concurrent.MVar as MVar
import qualified Control.Concurrent.STM  as STM
import qualified Control.Exception       as E
import qualified Control.Monad           as M

import qualified Criterion.Main  as CR
import qualified Criterion.Types as CR

async :: IO a -> IO (MVar.MVar a, CC.ThreadId)
async act = do
    mv <- MVar.newEmptyMVar
    thid <- CC.forkIO $ do
        M.void . M.forever . CC.threadDelay $ 1000000 * 1000000
        M.void $ act >>= MVar.tryPutMVar mv
    M.void . CC.forkIO $ do
        (act >>= MVar.putMVar mv) `E.finally` CC.killThread thid
    return (mv, thid)

wait :: (MVar.MVar a, CC.ThreadId) -> IO a
wait = MVar.readMVar . fst

type IterationSize  = Int
type QueueNum       = Int
type WriteAction    = Int -> IO ()
type ReadAction     = IO Int
type WriteThreadNum = Int
type ReadThreadNum  = Int
type ItemNum        = Int
type TestOne        = WriteThreadNum -> ReadThreadNum -> IO ()

data QueueActions v = forall q . QueueActions
    { newQueue   :: IO q
    , writeQueue :: q -> v -> IO ()
    , readQueue  :: q -> IO v
    }

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

testSpeed :: ItemNum -> QueueActions Int -> TestOne
testSpeed inum (QueueActions newAct writeAct readAct) wth rth = go
    where
        wnum = inum `div` wth
        rnum = inum `div` rth
        ws = [0..(wnum-1)] :: [Int]
        asyncw = M.replicateM wth . async
        asyncr = M.replicateM rth . async
        go = do
            q <- newAct
            wws <- asyncw $ M.forM_ ws $ writeAct q
            rws <- asyncr . M.replicateM_ rnum $ readAct q
            M.forM_ wws wait
            M.forM_ rws wait


testCost :: IterationSize -> QueueNum -> ItemNum -> QueueActions Int -> TestOne
testCost itersize qnum inum (QueueActions newAct writeAct readAct) wth rth = do
    itrq <- STM.newTQueueIO
    M.replicateM_ itersize . STM.atomically $ STM.writeTQueue itrq ()
    qws <- M.replicateM qnum . async $ go itrq
    M.forM_ qws wait
    where
        wnum = inum `div` wth
        rnum = inum `div` rth
        ws = [0..(wnum-1)] :: [Int]
        asyncw = M.replicateM wth . async
        asyncr = M.replicateM rth . async
        go itrq = do
            ma <- STM.atomically $ STM.tryReadTQueue itrq
            case ma of
                Nothing -> return ()
                Just () -> do
                    q <- newAct
                    wws <- asyncw $ M.forM_ ws $ writeAct q
                    rws <- asyncr . M.replicateM_ rnum $ readAct q
                    M.forM_ wws wait
                    M.forM_ rws wait
                    go itrq

testChan :: QueueActions a
testChan = QueueActions
    { newQueue   = Chan.newChan
    , writeQueue = Chan.writeChan
    , readQueue  = Chan.readChan
    }

{-
testUChan :: QueueActions a
testUChan = QueueActions
    { newQueue   = UNG.newChan
    , writeQueue = UNG.writeChan . fst
    , readQueue  = UNG.readChan  . snd
    }
--}

testTChan :: QueueActions a
testTChan = QueueActions
    { newQueue   = STM.newTChanIO
    , writeQueue = \ q -> STM.atomically . STM.writeTChan q
    , readQueue  = STM.atomically . STM.readTChan
    }

testTQueue :: QueueActions a
testTQueue = QueueActions
    { newQueue   = STM.newTQueueIO
    , writeQueue = \ q -> STM.atomically . STM.writeTQueue q
    , readQueue  = STM.atomically . STM.readTQueue
    }

testKZRQueue :: QueueActions a
testKZRQueue = QueueActions
    { newQueue   = KZR.newQueue
    , writeQueue = KZR.writeQueue
    , readQueue  = KZR.readQueue
    }

main :: IO ()
main = do
    CR.defaultMainWith configSpeed
        [
          CR.bgroup "KazuraQueue" $ testcases $ testSpeed_ testKZRQueue
        -- , CR.bgroup "UnagiChan"   $ testcases $ testSpeed_ testUChan
        , CR.bgroup "Chan"        $ testcases $ testSpeed_ testChan
        , CR.bgroup "TQueue"      $ testcases $ testSpeed_ testTQueue
        , CR.bgroup "TChan"       $ testcases $ testSpeed_ testTChan
        ]
    CR.defaultMainWith configCost
        [
          CR.bgroup "KazuraQueue" $ testcases $ testCost_ testKZRQueue
        -- , CR.bgroup "UnagiChan"   $ testcases $ testCost_ testUChan
        , CR.bgroup "Chan"        $ testcases $ testCost_ testChan
        , CR.bgroup "TQueue"      $ testcases $ testCost_ testTQueue
        , CR.bgroup "TChan"       $ testcases $ testCost_ testTChan
        ]
    where
        configCost = CR.defaultConfig
            { CR.reportFile = Just "report_cost.html" }
        configSpeed = CR.defaultConfig
            { CR.reportFile = Just "report_speed.html" }
        testSpeed_ = testSpeed 90000
        testCost_ = testCost 20 10 45000
        testcases test =
            [ testcase 1 1 test
            , testcase 3 1 test
            , testcase 1 3 test
            ]
        testcase wth rth test =
            CR.bench (show wth ++ "." ++ show rth) $
                CR.nfIO $ test wth rth