@@ -23,6 +23,7 @@ import Data.Aeson.Parser (scientific)
23import qualified Data.Attoparsec.ByteString.Lazy as AttoL
23import qualified Data.Attoparsec.ByteString.Lazy as AttoL
24import qualified Data.Attoparsec.ByteString.Char8 as Atto8
24import qualified Data.Attoparsec.ByteString.Char8 as Atto8
25import qualified Data.Aeson as A
25import qualified Data.Aeson as A
26+import qualified Data.ByteString as BS
26import qualified Data.ByteString.Lazy as LBS
27import qualified Data.ByteString.Lazy as LBS
27import qualified Data.ByteString.Lazy.Char8 as LBS8
28import qualified Data.ByteString.Lazy.Char8 as LBS8
@@ -35,9 +36,8 @@ decodeString = A.decode
35decodeScientific :: LBS.ByteString -> Maybe Scientific
36decodeScientific :: LBS.ByteString -> Maybe Scientific
36decodeScientific = A.decode
37decodeScientific = A.decode
38−decodeRaw :: LBS.ByteString -> Integer
39+decodeViaRead :: LBS.ByteString -> Integer
39−decodeRaw = LBS.foldl' step 0 where
40+decodeViaRead = read . LBS8.unpack
40− step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
42decodeAtto :: LBS.ByteString -> Maybe Scientific
42decodeAtto :: LBS.ByteString -> Maybe Scientific
@@ -102,9 +102,63 @@ main = defaultMain
103 benchPair name input = bgroup name
103 benchPair name input = bgroup name
104 [ bench "Int" $ whnf decodeInt input
104 [ bench "Int" $ whnf decodeInt input
105− , bench "RawFold" $ whnf decodeRaw input
105+ , bench "Simple" $ whnf bsToIntegerSimple (LBS.toStrict input)
106+ , bench "Optim" $ whnf bsToInteger (LBS.toStrict input)
107+ , bench "Read" $ whnf decodeViaRead input
106 , bench "Scientific" $ whnf decodeScientific input
108 , bench "Scientific" $ whnf decodeScientific input
107 , bench "parserA" $ whnf decodeAtto input
109 , bench "parserA" $ whnf decodeAtto input
108 , bench "parserS" $ whnf decodeAtto8 input
110 , bench "parserS" $ whnf decodeAtto8 input
109 , bench "String" $ whnf decodeString $ "\"" <> input <> "\""
111 , bench "String" $ whnf decodeString $ "\"" <> input <> "\""
114+-------------------------------------------------------------------------------
115+-- better fromInteger
116+-------------------------------------------------------------------------------
118+bsToInteger :: BS.ByteString -> Integer
120+ | l > 40 = valInteger 10 l [ fromIntegral (w - 48) | w <- BS.unpack bs ]
121+ | otherwise = bsToIntegerSimple bs
125+bsToIntegerSimple :: BS.ByteString -> Integer
126+bsToIntegerSimple = BS.foldl' step 0 where
127+ step a b = a * 10 + fromIntegral (b - 48) -- 48 = '0'
129+-- A sub-quadratic algorithm for Integer. Pairs of adjacent radix b
130+-- digits are combined into a single radix b^2 digit. This process is
131+-- repeated until we are left with a single digit. This algorithm
132+-- performs well only on large inputs, so we use the simple algorithm
133+-- for smaller inputs.
134+valInteger :: Integer -> Int -> [Integer] -> Integer
137+ go :: Integer -> Int -> [Integer] -> Integer
141+ | l > 40 = b' `seq` go b' l' (combine b ds')
142+ | otherwise = valSimple b ds
144+ -- ensure that we have an even number of digits
145+ -- before we call combine:
146+ ds' = if even l then ds else 0 : ds
148+ l' = (l + 1) `quot` 2
150+ combine b (d1 : d2 : ds) = d `seq` (d : combine b ds)
154+ combine _ [_] = errorWithoutStackTrace "this should not happen"
156+-- The following algorithm is only linear for types whose Num operations
157+-- are in constant time.
158+valSimple :: Integer -> [Integer] -> Integer
159+valSimple base = go 0
162+ go r (d : ds) = r' `seq` go r' ds
164+ r' = r * base + fromIntegral d