binary-0.8.0.0: benchmarks/Get.hs
{-# LANGUAGE CPP, OverloadedStrings, ExistentialQuantification, BangPatterns #-}
#if defined(__GLASGOW_HASKELL__) && !defined(__HADDOCK__)
#include "MachDeps.h"
#endif
module Main where
import Control.DeepSeq
import Control.Exception (evaluate)
import Criterion.Main
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as C8
import qualified Data.ByteString.Lazy as L
import Data.Bits
import Data.Char (ord)
import Data.List (foldl')
import Control.Applicative
import Data.Binary
import Data.Binary.Get
import qualified Data.Serialize.Get as Cereal
import qualified Data.Attoparsec.ByteString as A
import qualified Data.Attoparsec.ByteString.Lazy as AL
#if !MIN_VERSION_bytestring(0,10,0)
instance NFData S.ByteString
instance NFData L.ByteString where
rnf = rnf . L.toChunks
#endif
main :: IO ()
main = do
evaluate $ rnf [
rnf brackets,
rnf bracketsInChunks,
rnf bracketCount,
rnf oneMegabyte,
rnf oneMegabyteLBS,
rnf manyBytes,
rnf encodedBigInteger
]
defaultMain
[ bgroup "brackets"
[ bench "Binary 100kb, one chunk" $
whnf (checkBracket . runTest bracketParser) brackets
, bench "Binary 100kb, 100 byte chunks" $
whnf (checkBracket . runTest bracketParser) bracketsInChunks
, bench "Attoparsec lazy-bs 100kb, one chunk" $
whnf (checkBracket . runAttoL bracketParser_atto) brackets
, bench "Attoparsec lazy-bs 100kb, 100 byte chunks" $
whnf (checkBracket . runAttoL bracketParser_atto) bracketsInChunks
, bench "Attoparsec strict-bs 100kb" $
whnf (checkBracket . runAtto bracketParser_atto) $ S.concat (L.toChunks brackets)
, bench "Cereal strict-bs 100kb" $
whnf (checkBracket . runCereal bracketParser_cereal) $ S.concat (L.toChunks brackets)
]
, bgroup "comparison getStruct4, 1MB of struct of 4 Word8s"
[ bench "Attoparsec" $
whnf (runAtto (getStruct4_atto mega)) oneMegabyte
, bench "Binary" $
whnf (runTest (getStruct4 mega)) oneMegabyteLBS
, bench "Cereal" $
whnf (runCereal (getStruct4_cereal mega)) oneMegabyte
]
, bgroup "comparison getWord8, 1MB"
[ bench "Attoparsec" $
whnf (runAtto (getWord8N1_atto mega)) oneMegabyte
, bench "Binary" $
whnf (runTest (getWord8N1 mega)) oneMegabyteLBS
, bench "Cereal" $
whnf (runCereal (getWord8N1_cereal mega)) oneMegabyte
]
, bgroup "getWord8 1MB"
[ bench "chunk size 2 bytes" $
whnf (runTest (getWord8N2 mega)) oneMegabyteLBS
, bench "chunk size 4 bytes" $
whnf (runTest (getWord8N4 mega)) oneMegabyteLBS
, bench "chunk size 8 bytes" $
whnf (runTest (getWord8N8 mega)) oneMegabyteLBS
, bench "chunk size 16 bytes" $
whnf (runTest (getWord8N16 mega)) oneMegabyteLBS
]
, bgroup "getWord8 1MB Applicative"
[ bench "chunk size 2 bytes" $
whnf (runTest (getWord8N2A mega)) oneMegabyteLBS
, bench "chunk size 4 bytes" $
whnf (runTest (getWord8N4A mega)) oneMegabyteLBS
, bench "chunk size 8 bytes" $
whnf (runTest (getWord8N8A mega)) oneMegabyteLBS
, bench "chunk size 16 bytes" $
whnf (runTest (getWord8N16A mega)) oneMegabyteLBS
]
, bgroup "roll"
[ bench "foldr" $ nf (roll_foldr :: [Word8] -> Integer) manyBytes
, bench "foldl'" $ nf (roll_foldl' :: [Word8] -> Integer) manyBytes
]
, bgroup "Integer"
[ bench "decode" $ nf (decode :: L.ByteString -> Integer) encodedBigInteger
]
]
checkBracket :: Int -> Int
checkBracket x | x == bracketCount = x
| otherwise = error "argh!"
runTest :: Get a -> L.ByteString -> a
runTest decoder inp = runGet decoder inp
runCereal :: Cereal.Get a -> C8.ByteString -> a
runCereal decoder inp = case Cereal.runGet decoder inp of
Right a -> a
Left err -> error err
runAtto :: AL.Parser a -> C8.ByteString -> a
runAtto decoder inp = case A.parseOnly decoder inp of
Right a -> a
Left err -> error err
runAttoL :: Show a => AL.Parser a -> L.ByteString -> a
runAttoL decoder inp = case AL.parse decoder inp of
AL.Done _ r -> r
a -> error (show a)
-- Defs.
oneMegabyte :: S.ByteString
oneMegabyte = S.replicate mega $ fromIntegral $ ord 'a'
oneMegabyteLBS :: L.ByteString
oneMegabyteLBS = L.fromChunks [oneMegabyte]
mega :: Int
mega = 1024 * 1024
-- 100k of brackets
bracketTest :: L.ByteString -> Int
bracketTest inp = runTest bracketParser inp
bracketCount :: Int
bracketCount = fromIntegral $ L.length brackets `div` 2
brackets :: L.ByteString
brackets = L.fromChunks [C8.concat (L.toChunks bracketsInChunks)]
bracketsInChunks :: L.ByteString
bracketsInChunks = L.fromChunks (replicate chunksOfBrackets oneChunk)
where
oneChunk = "((()((()()))((()(()()()()()()()(((()()()()(()()(()(()())))))()((())())))()())(((())())(()))))()(()))"
chunksOfBrackets = 102400 `div` S.length oneChunk
bracketParser :: Get Int
bracketParser = cont <|> return 0
where
cont = do v <- some ( do 40 <- getWord8
n <- many cont
41 <- getWord8
return $! sum n + 1)
return $! sum v
bracketParser_cereal :: Cereal.Get Int
bracketParser_cereal = cont <|> return 0
where
cont = do v <- some ( do 40 <- Cereal.getWord8
n <- many cont
41 <- Cereal.getWord8
return $! sum n + 1)
return $! sum v
bracketParser_atto :: A.Parser Int
bracketParser_atto = cont <|> return 0
where
cont = do v <- some ( do _ <- A.word8 40
n <- bracketParser_atto
_ <- A.word8 41
return $! n + 1)
return $! sum v
-- Strict struct of 4 Word8s
data S2 = S2 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
data S4 = S4 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
data S8 = S8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
data S16 = S16 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
{-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8 {-# UNPACK #-} !Word8
getStruct4 :: Int -> Get [S4]
getStruct4 = loop []
where loop acc 0 = return acc
loop acc n = do
!w0 <- getWord8
!w1 <- getWord8
!w2 <- getWord8
!w3 <- getWord8
let !s = S4 w0 w1 w2 w3
loop (s : acc) (n - 4)
getStruct4_cereal :: Int -> Cereal.Get [S4]
getStruct4_cereal = loop []
where loop acc 0 = return acc
loop acc n = do
!w0 <- Cereal.getWord8
!w1 <- Cereal.getWord8
!w2 <- Cereal.getWord8
!w3 <- Cereal.getWord8
let !s = S4 w0 w1 w2 w3
loop (s : acc) (n - 4)
getStruct4_atto :: Int -> A.Parser [S4]
getStruct4_atto = loop []
where loop acc 0 = return acc
loop acc n = do
!w0 <- A.anyWord8
!w1 <- A.anyWord8
!w2 <- A.anyWord8
!w3 <- A.anyWord8
let !s = S4 w0 w1 w2 w3
loop (s : acc) (n - 4)
getWord8N1 :: Int -> Get [Word8]
getWord8N1 = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- getWord8
loop (s0:s) (n-1)
getWord8N1_cereal :: Int -> Cereal.Get [Word8]
getWord8N1_cereal = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- Cereal.getWord8
loop (s0:s) (n-1)
getWord8N1_atto :: Int -> A.Parser [Word8]
getWord8N1_atto = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- A.anyWord8
loop (s0:s) (n-1)
getWord8N2 :: Int -> Get [S2]
getWord8N2 = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- getWord8
s1 <- getWord8
let !v = S2 s0 s1
loop (v:s) (n-2)
getWord8N2A :: Int -> Get [S2]
getWord8N2A = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
!v <- S2 <$> getWord8 <*> getWord8
loop (v:s) (n-2)
getWord8N4 :: Int -> Get [S4]
getWord8N4 = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- getWord8
s1 <- getWord8
s2 <- getWord8
s3 <- getWord8
let !v = S4 s0 s1 s2 s3
loop (v:s) (n-4)
getWord8N4A :: Int -> Get [S4]
getWord8N4A = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
!v <- S4 <$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8
loop (v:s) (n-4)
getWord8N8 :: Int -> Get [S8]
getWord8N8 = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- getWord8
s1 <- getWord8
s2 <- getWord8
s3 <- getWord8
s4 <- getWord8
s5 <- getWord8
s6 <- getWord8
s7 <- getWord8
let !v = S8 s0 s1 s2 s3 s4 s5 s6 s7
loop (v:s) (n-8)
getWord8N8A :: Int -> Get [S8]
getWord8N8A = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
!v <- S8 <$> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
loop (v:s) (n-8)
getWord8N16 :: Int -> Get [S16]
getWord8N16 = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
s0 <- getWord8
s1 <- getWord8
s2 <- getWord8
s3 <- getWord8
s4 <- getWord8
s5 <- getWord8
s6 <- getWord8
s7 <- getWord8
s8 <- getWord8
s9 <- getWord8
s10 <- getWord8
s11 <- getWord8
s12 <- getWord8
s13 <- getWord8
s14 <- getWord8
s15 <- getWord8
let !v = S16 s0 s1 s2 s3 s4 s5 s6 s7 s8 s9 s10 s11 s12 s13 s14 s15
loop (v:s) (n-16)
getWord8N16A :: Int -> Get [S16]
getWord8N16A = loop []
where loop s n | s `seq` n `seq` False = undefined
loop s 0 = return s
loop s n = do
!v <- S16 <$> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
loop (v:s) (n-16)
manyBytes :: [Word8]
manyBytes = concat $ replicate 256 [0..255]
bigInteger :: Integer
bigInteger = roll_foldl' manyBytes
encodedBigInteger :: L.ByteString
encodedBigInteger = encode bigInteger
roll_foldr :: (Integral a, Num a, Bits a) => [Word8] -> a
roll_foldr = foldr unstep 0
where
unstep b a = a `shiftL` 8 .|. fromIntegral b
roll_foldl' :: (Integral a, Num a, Bits a) => [Word8] -> a
roll_foldl' = foldl' unstep 0 . reverse
where
unstep a b = a `shiftL` 8 .|. fromIntegral b