superbuffer-0.2.0.0: bench/Bench.hs
module Main where
import Control.Concurrent.Async
import Control.Monad
import Criterion
import Criterion.Main
import Data.ByteString.SuperBuffer
import Data.Int
import qualified Data.BufferBuilder as BB
import qualified Data.ByteString as BS
import qualified Data.ByteString.Builder as BSB
import qualified Data.ByteString.Lazy as BSL
main :: IO ()
main =
defaultMain
[ bgroup "main"
[ mkGroup "small" 5000 4000
, mkGroup "med" 500 40000
, mkGroup "large" 50 400000
]
]
mkGroup :: String -> Int -> Int -> Benchmark
mkGroup name steps chunkSize =
bgroup name $
mkSizedGroup steps chunkSize bufName buildBuf
++ mkSizedGroup steps chunkSize bufNameT buildBufT
++ mkSizedGroup steps chunkSize bufBBName buildBufBB
++
[ bench "bytestring builder" $ nfIO $ BS.reverse <$> buildBufBuilder steps chunkSize
, bench "bytestring fromChunks" $ nfIO $ BS.reverse <$> buildBufChunks steps chunkSize
, bench "bytestring concat" $ nfIO $ BS.reverse <$> buildBufConcat steps chunkSize
]
where
bufBBName is = "buffer-builder (init=" ++ show is ++ " bytes, trim=yes)"
bufName is = "superbuffer (init=" ++ show is ++ " bytes)"
bufNameT is = "superbuffer (init=" ++ show is ++ " bytes, threadsafe, 2 concurrent writes)"
mkSizedGroup ::
Int -> Int -> (Int64 -> String) -> (Int64 -> Int -> Int -> IO BS.ByteString) -> [Benchmark]
mkSizedGroup steps chunkSize bufName builder =
[ bench (bufName iBufSize128) $ nfIO $ BS.reverse <$> builder iBufSize128 steps chunkSize
, bench (bufName iBufSize) $ nfIO $ BS.reverse <$> builder iBufSize steps chunkSize
, bench (bufName iBufSize2) $ nfIO $ BS.reverse <$> builder iBufSize2 steps chunkSize
, bench (bufName iBufSize4) $ nfIO $ BS.reverse <$> builder iBufSize4 steps chunkSize
, bench (bufName iBufSizeAll) $ nfIO $ BS.reverse <$> builder iBufSizeAll steps chunkSize
]
where
iBufSize128 = 128
iBufSize = fromIntegral chunkSize
iBufSize2 = 2 * fromIntegral chunkSize
iBufSize4 = 4 * fromIntegral chunkSize
iBufSizeAll = fromIntegral $ steps * chunkSize
mkChunk :: Int -> Int -> BS.ByteString
mkChunk step chunkSize =
BS.replicate chunkSize (fromIntegral $ (step `mod` 100) + 50)
{-# INLINE mkChunk #-}
buildBuf :: Int64 -> Int -> Int -> IO BS.ByteString
buildBuf bufSize steps chunkSize =
withBuffer bufSize $ \buf ->
forM_ [0..steps] $ \step ->
appendBuffer buf (mkChunk step chunkSize)
buildBufT :: Int64 -> Int -> Int -> IO BS.ByteString
buildBufT bufSize steps chunkSize =
withBuffer bufSize $ \buf ->
forM_ [0..(ceiling halfSteps)] $ \step ->
concurrently_
(appendBufferT buf (mkChunk step chunkSize))
(appendBufferT buf (mkChunk step chunkSize))
where
halfSteps :: Double
halfSteps = fromIntegral steps / 2.0
buildBufBB :: Int64 -> Int -> Int -> IO BS.ByteString
buildBufBB bufSize steps chunkSize =
pure $ BB.runBufferBuilderWithOptions opts $
forM_ [0..steps] $ \step ->
BB.appendBS (mkChunk step chunkSize)
where
opts =
BB.Options
{ BB.initialCapacity = fromIntegral bufSize
, BB.trimFinalBuffer = True
}
buildBufBuilder :: Int -> Int -> IO BS.ByteString
buildBufBuilder steps chunkSize =
BSL.toStrict . BSB.toLazyByteString <$>
foldM (\b a -> pure $ b `mappend` BSB.byteString (mkChunk a chunkSize)) mempty [0..steps]
buildBufChunks :: Int -> Int -> IO BS.ByteString
buildBufChunks steps chunkSize =
BSL.toStrict . BSL.fromChunks <$> (
forM [0..steps] $ \step ->
pure (mkChunk step chunkSize))
buildBufConcat :: Int -> Int -> IO BS.ByteString
buildBufConcat steps chunkSize =
BS.concat <$> (
forM [0..steps] $ \step ->
pure (mkChunk step chunkSize))