aeson-1.4.2.0: benchmarks/Issue673.hs
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (
main,
input17,
input32,
input64,
input128,
input256,
input2048,
input4096,
input8192,
input16384,
) where
import Criterion.Main
import Prelude.Compat
import Data.Int (Int64)
import Data.Scientific (Scientific)
import Data.Aeson.Parser (scientific)
import qualified Data.Attoparsec.ByteString.Lazy as AttoL
import qualified Data.Attoparsec.ByteString.Char8 as Atto8
import qualified Data.Aeson as A
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Lazy.Char8 as LBS8
decodeInt :: LBS.ByteString -> Maybe Int
decodeInt = A.decode
decodeString :: LBS.ByteString -> Maybe String
decodeString = A.decode
decodeScientific :: LBS.ByteString -> Maybe Scientific
decodeScientific = A.decode
decodeViaRead :: LBS.ByteString -> Integer
decodeViaRead = read . LBS8.unpack
decodeAtto :: LBS.ByteString -> Maybe Scientific
decodeAtto
= parseOnly (scientific <* AttoL.endOfInput)
where
parseOnly p lbs = case AttoL.parse p lbs of
AttoL.Done _ r -> Just r
AttoL.Fail {} -> Nothing
decodeAtto8 :: LBS.ByteString -> Maybe Scientific
decodeAtto8
= parseOnly (Atto8.scientific <* AttoL.endOfInput)
where
parseOnly p lbs = case AttoL.parse p lbs of
AttoL.Done _ r -> Just r
AttoL.Fail {} -> Nothing
generate :: Int64 -> LBS.ByteString
generate n = LBS8.replicate n '1'
input17 :: LBS.ByteString
input17 = generate 17
input32 :: LBS.ByteString
input32 = generate 32
input64 :: LBS.ByteString
input64 = generate 64
input128 :: LBS.ByteString
input128 = generate 128
input256 :: LBS.ByteString
input256 = generate 256
input2048 :: LBS.ByteString
input2048 = generate 2048
input4096 :: LBS.ByteString
input4096 = generate 4096
input8192 :: LBS.ByteString
input8192 = generate 8192
input16384 :: LBS.ByteString
input16384 = generate 16384
main :: IO ()
main = defaultMain
-- works on 64bit
[ benchPair "17" input17
-- , benchPair "32" input32
-- , benchPair "64" input64
-- , benchPair "128" input128
-- , benchPair "256" input256
, benchPair "2048" input2048
, benchPair "4096" input4096
, benchPair "8192" input8192
, benchPair "16384" input16384
]
where
benchPair name input = bgroup name
[ bench "Int" $ whnf decodeInt input
, bench "Simple" $ whnf bsToIntegerSimple (LBS.toStrict input)
, bench "Optim" $ whnf bsToInteger (LBS.toStrict input)
, bench "Read" $ whnf decodeViaRead input
, bench "Scientific" $ whnf decodeScientific input
, bench "parserA" $ whnf decodeAtto input
, bench "parserS" $ whnf decodeAtto8 input
, bench "String" $ whnf decodeString $ "\"" <> input <> "\""
]
-------------------------------------------------------------------------------
-- better fromInteger
-------------------------------------------------------------------------------
bsToInteger :: BS.ByteString -> Integer
bsToInteger bs
| l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]
| otherwise = bsToIntegerSimple bs
where
l = BS.length bs
bsToIntegerSimple :: BS.ByteString -> Integer
bsToIntegerSimple = BS.foldl' step 0 where
step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
-- digits are combined into a single radix b^2 digit. This process is
-- repeated until we are left with a single digit. This algorithm
-- performs well only on large inputs, so we use the simple algorithm
-- for smaller inputs.
valInteger :: Integer -> Int -> [Integer] -> Integer
valInteger = go
where
go :: Integer -> Int -> [Integer] -> Integer
go _ _ [] = 0
go _ _ [d] = d
go b l ds
| l > 40 = b' `seq` go b' l' (combine b ds')
| otherwise = valSimple b ds
where
-- ensure that we have an even number of digits
-- before we call combine:
ds' = if even l then ds else 0 : ds
b' = b * b
l' = (l + 1) `quot` 2
combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
where
d = d1 * b + d2
combine _ [] = []
combine _ [_] = errorWithoutStackTrace "this should not happen"
-- The following algorithm is only linear for types whose Num operations
-- are in constant time.
valSimple :: Integer -> [Integer] -> Integer
valSimple base = go 0
where
go r [] = r
go r (d : ds) = r' `seq` go r' ds
where
r' = r * base + fromIntegral d