packages feed

bytestring-0.10.0.0: bench/BenchAll.hs

{-# LANGUAGE PackageImports, ScopedTypeVariables, BangPatterns #-}
-- |
-- Copyright   : (c) 2011 Simon Meier
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Simon Meier <iridcode@gmail.com>
-- Stability   : experimental
-- Portability : tested on GHC only
--
-- Benchmark all 'Builder' functions.
module Main (main) where

import Prelude hiding (words)
import Criterion.Main
import Data.Foldable (foldMap)

import qualified Data.ByteString                  as S
import qualified Data.ByteString.Lazy             as L

import           Data.ByteString.Lazy.Builder
import           Data.ByteString.Lazy.Builder.ASCII
import           Data.ByteString.Lazy.Builder.BasicEncoding
                   ( FixedEncoding, BoundedEncoding, (>$<) )
import qualified Data.ByteString.Lazy.Builder.BasicEncoding          as E
import qualified Data.ByteString.Lazy.Builder.BasicEncoding.Internal as EI

import Foreign

------------------------------------------------------------------------------
-- Benchmark support
------------------------------------------------------------------------------

countToZero :: Int -> Maybe (Int, Int)
countToZero 0 = Nothing
countToZero n = Just (n, n - 1)


------------------------------------------------------------------------------
-- Benchmark
------------------------------------------------------------------------------

-- input data (NOINLINE to ensure memoization)
----------------------------------------------

-- | Few-enough repetitions to avoid making GC too expensive.
nRepl :: Int
nRepl = 10000

{-# NOINLINE intData #-}
intData :: [Int]
intData = [1..nRepl]

-- Half of the integers inside the range of an Int and half of them outside.
{-# NOINLINE integerData #-}
integerData :: [Integer]
integerData = map (\x -> fromIntegral x + fromIntegral (maxBound - nRepl `div` 2)) intData

{-# NOINLINE floatData #-}
floatData :: [Float]
floatData = map (\x -> (3.14159 * fromIntegral x) ^ (3 :: Int)) intData

{-# NOINLINE doubleData #-}
doubleData :: [Double]
doubleData = map (\x -> (3.14159 * fromIntegral x) ^ (3 :: Int)) intData

{-# NOINLINE byteStringData #-}
byteStringData :: S.ByteString
byteStringData = S.pack $ map fromIntegral intData

{-# NOINLINE lazyByteStringData #-}
lazyByteStringData :: L.ByteString
lazyByteStringData = case S.splitAt (nRepl `div` 2) byteStringData of
    (bs1, bs2) -> L.fromChunks [bs1, bs2]


-- benchmark wrappers
---------------------

{-# INLINE benchB #-}
benchB :: String -> a -> (a -> Builder) -> Benchmark
benchB name x b =
    bench (name ++" (" ++ show nRepl ++ ")") $
        whnf (L.length . toLazyByteString . b) x

{-# INLINE benchBInts #-}
benchBInts :: String -> ([Int] -> Builder) -> Benchmark
benchBInts name = benchB name intData

-- | Benchmark a 'FixedEncoding'. Full inlining to enable specialization.
{-# INLINE benchFE #-}
benchFE :: String -> FixedEncoding Int -> Benchmark
benchFE name = benchBE name . E.fromF

-- | Benchmark a 'BoundedEncoding'. Full inlining to enable specialization.
{-# INLINE benchBE #-}
benchBE :: String -> BoundedEncoding Int -> Benchmark
benchBE name e =
  bench (name ++" (" ++ show nRepl ++ ")") $ benchIntEncodingB nRepl e

-- We use this construction of just looping through @n,n-1,..,1@ to ensure that
-- we measure the speed of the encoding and not the speed of generating the
-- values to be encoded.
{-# INLINE benchIntEncodingB #-}
benchIntEncodingB :: Int                  -- ^ Maximal 'Int' to write
                  -> BoundedEncoding Int  -- ^ 'BoundedEncoding' to execute
                  -> IO ()                -- ^ 'IO' action to benchmark
benchIntEncodingB n0 w
  | n0 <= 0   = return ()
  | otherwise = do
      fpbuf <- mallocForeignPtrBytes (n0 * EI.sizeBound w)
      withForeignPtr fpbuf (loop n0) >> return ()
  where
    loop !n !op
      | n <= 0    = return op
      | otherwise = EI.runB w n op >>= loop (n - 1)



-- benchmarks
-------------

sanityCheckInfo :: [String]
sanityCheckInfo =
  [ "Sanity checks:"
  , " lengths of input data: " ++ show
      [ length intData, length floatData, length doubleData, length integerData
      , S.length byteStringData, fromIntegral (L.length lazyByteStringData)
      ]
  ]

main :: IO ()
main = do
  mapM_ putStrLn sanityCheckInfo
  putStrLn ""
  Criterion.Main.defaultMain
    [ bgroup "Data.ByteString.Lazy.Builder"
      [ bgroup "Encoding wrappers"
        [ benchBInts "foldMap word8" $
            foldMap (word8 . fromIntegral)
        , benchBInts "encodeListWithF word8" $
            E.encodeListWithF (fromIntegral >$< E.word8)
        , benchB     "encodeUnfoldrWithF word8" nRepl $
            E.encodeUnfoldrWithF (fromIntegral >$< E.word8) countToZero
        , benchB     "encodeByteStringWithF word8" byteStringData $
            E.encodeByteStringWithF E.word8
        , benchB     "encodeLazyByteStringWithF word8" lazyByteStringData $
            E.encodeLazyByteStringWithF E.word8
        ]

      , bgroup "Non-bounded encodings"
        [ benchB "foldMap floatDec"        floatData          $ foldMap floatDec
        , benchB "foldMap doubleDec"       doubleData         $ foldMap doubleDec
        , benchB "foldMap integerDec"      integerData        $ foldMap integerDec
        , benchB "byteStringHexFixed"      byteStringData     $ byteStringHexFixed
        , benchB "lazyByteStringHexFixed"  lazyByteStringData $ lazyByteStringHexFixed
        ]
      ]

    , bgroup "Data.ByteString.Lazy.Builder.BasicEncoding"
      [ benchFE "char7"      $ toEnum       >$< E.char7
      , benchFE "char8"      $ toEnum       >$< E.char8
      , benchBE "charUtf8"   $ toEnum       >$< E.charUtf8

      -- binary encoding
      , benchFE "int8"       $ fromIntegral >$< E.int8
      , benchFE "word8"      $ fromIntegral >$< E.word8

      -- big-endian
      , benchFE "int16BE"    $ fromIntegral >$< E.int16BE
      , benchFE "int32BE"    $ fromIntegral >$< E.int32BE
      , benchFE "int64BE"    $ fromIntegral >$< E.int64BE

      , benchFE "word16BE"   $ fromIntegral >$< E.word16BE
      , benchFE "word32BE"   $ fromIntegral >$< E.word32BE
      , benchFE "word64BE"   $ fromIntegral >$< E.word64BE

      , benchFE "floatBE"    $ fromIntegral >$< E.floatBE
      , benchFE "doubleBE"   $ fromIntegral >$< E.doubleBE

      -- little-endian
      , benchFE "int16LE"    $ fromIntegral >$< E.int16LE
      , benchFE "int32LE"    $ fromIntegral >$< E.int32LE
      , benchFE "int64LE"    $ fromIntegral >$< E.int64LE

      , benchFE "word16LE"   $ fromIntegral >$< E.word16LE
      , benchFE "word32LE"   $ fromIntegral >$< E.word32LE
      , benchFE "word64LE"   $ fromIntegral >$< E.word64LE

      , benchFE "floatLE"    $ fromIntegral >$< E.floatLE
      , benchFE "doubleLE"   $ fromIntegral >$< E.doubleLE

      -- host-dependent
      , benchFE "int16Host"  $ fromIntegral >$< E.int16Host
      , benchFE "int32Host"  $ fromIntegral >$< E.int32Host
      , benchFE "int64Host"  $ fromIntegral >$< E.int64Host
      , benchFE "intHost"    $ fromIntegral >$< E.intHost

      , benchFE "word16Host" $ fromIntegral >$< E.word16Host
      , benchFE "word32Host" $ fromIntegral >$< E.word32Host
      , benchFE "word64Host" $ fromIntegral >$< E.word64Host
      , benchFE "wordHost"   $ fromIntegral >$< E.wordHost

      , benchFE "floatHost"  $ fromIntegral >$< E.floatHost
      , benchFE "doubleHost" $ fromIntegral >$< E.doubleHost
      ]

    , bgroup "Data.ByteString.Lazy.Builder.BoundedEncoding.ASCII"
      [
      -- decimal number
        benchBE "int8Dec"     $ fromIntegral >$< E.int8Dec
      , benchBE "int16Dec"    $ fromIntegral >$< E.int16Dec
      , benchBE "int32Dec"    $ fromIntegral >$< E.int32Dec
      , benchBE "int64Dec"    $ fromIntegral >$< E.int64Dec
      , benchBE "intDec"      $ fromIntegral >$< E.intDec

      , benchBE "word8Dec"    $ fromIntegral >$< E.word8Dec
      , benchBE "word16Dec"   $ fromIntegral >$< E.word16Dec
      , benchBE "word32Dec"   $ fromIntegral >$< E.word32Dec
      , benchBE "word64Dec"   $ fromIntegral >$< E.word64Dec
      , benchBE "wordDec"     $ fromIntegral >$< E.wordDec

      -- hexadecimal number
      , benchBE "word8Hex"    $ fromIntegral >$< E.word8Hex
      , benchBE "word16Hex"   $ fromIntegral >$< E.word16Hex
      , benchBE "word32Hex"   $ fromIntegral >$< E.word32Hex
      , benchBE "word64Hex"   $ fromIntegral >$< E.word64Hex
      , benchBE "wordHex"     $ fromIntegral >$< E.wordHex

      -- fixed-width hexadecimal numbers
      , benchFE "int8HexFixed"     $ fromIntegral >$< E.int8HexFixed
      , benchFE "int16HexFixed"    $ fromIntegral >$< E.int16HexFixed
      , benchFE "int32HexFixed"    $ fromIntegral >$< E.int32HexFixed
      , benchFE "int64HexFixed"    $ fromIntegral >$< E.int64HexFixed

      , benchFE "word8HexFixed"    $ fromIntegral >$< E.word8HexFixed
      , benchFE "word16HexFixed"   $ fromIntegral >$< E.word16HexFixed
      , benchFE "word32HexFixed"   $ fromIntegral >$< E.word32HexFixed
      , benchFE "word64HexFixed"   $ fromIntegral >$< E.word64HexFixed

      , benchFE "floatHexFixed"    $ fromIntegral >$< E.floatHexFixed
      , benchFE "doubleHexFixed"   $ fromIntegral >$< E.doubleHexFixed
      ]
    ]