{-# 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