packages feed

stm-ringbuffer-0.1.0.0: benchmark/StmBench.hs

-- Adapted from STM benchmarks: https://github.com/haskell/stm/blob/master/bench/ChanBench.hs

{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}

module StmBench (stmBench) where

import Control.Concurrent.STM
    ( atomically, newTBQueueIO, readTBQueue, writeTBQueue, TBQueue )
import Control.Concurrent.STM.TRingBuffer.TBQueue qualified as RB
import Control.Concurrent.Async ( async, wait )
import Control.Monad ( replicateM, replicateM_ )
import Test.Tasty (localOption)
import Test.Tasty.Bench
    ( bench, bgroup, defaultMain, whnfAppIO, TimeMode(WallTime) )
import Data.Foldable (traverse_)


stmBench :: IO ()
stmBench = defaultMain
    [ localOption WallTime $ bgroup "concurrent spsc"
        [ bench "TBQueue" $ whnfAppIO (concurrentSpsc @TBQueue) n
        , bench "RB.TBQueue" $ whnfAppIO (concurrentSpsc @RB.TBQueue) n
        ]
    , localOption WallTime $ bgroup "concurrent mpmc"
        [ bench "TBQueue" $ whnfAppIO (concurrentMpmc @TBQueue) n
        , bench "RB.TBQueue" $ whnfAppIO (concurrentMpmc @RB.TBQueue) n
        ]
    , bgroup "burst"
        [ bench "TBQueue" $ whnfAppIO (burst @TBQueue 1000) n
        , bench "RB.TBQueue" $ whnfAppIO (burst @RB.TBQueue 1000) n
        ]
    ]
  where
    n = 2000000

class Channel c where
    newc :: IO (c a)
    readc :: c a -> IO a
    writec :: c a -> a -> IO ()

instance Channel TBQueue where
    newc = newTBQueueIO 4096
    readc c = atomically $ readTBQueue c
    writec c x = atomically $ writeTBQueue c x

instance Channel RB.TBQueue where
    newc = RB.newTBQueueIO 4096
    readc c = atomically $ RB.readTBQueue c
    writec c x = atomically $ RB.writeTBQueue c x

-- concurrent writing and reading with single producer, single consumer
concurrentSpsc :: forall c. (Channel c) => Int -> IO ()
concurrentSpsc n = do
    c :: c Int <- newc
    writer <- async $ replicateM_ n $ writec c 1
    reader <- async $ replicateM_ n $ readc c
    wait writer
    wait reader

-- concurrent writing and reading with multiple producers, multiple consumers
concurrentMpmc :: forall c. (Channel c) => Int -> IO ()
concurrentMpmc n = do
    c :: c Int <- newc
    writers <- replicateM 10 $ async $ replicateM_ (n `div` 10) $ writec c 1
    readers <- replicateM 10 $ async $ replicateM_ (n `div` 10) $ readc c
    traverse_ wait writers
    traverse_ wait readers

-- bursts of bulk writes, then bulk reads
burst :: forall c. (Channel c) => Int -> Int -> IO ()
burst k n = do
    c :: c Int <- newc
    replicateM_ k $ do
        replicateM_ (n `div` k) $ writec c 1
        replicateM_ (n `div` k) $ readc c