2-- Module : Streamly.Benchmark.Data.ParserD
3-- Copyright : (c) 2020 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
8{-# LANGUAGE FlexibleContexts #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# OPTIONS_GHC -fspec-constr-recursive=4 #-}
17import Control.DeepSeq (NFData(..))
18import Control.Monad.Catch (MonadCatch, MonadThrow)
19import Data.Foldable (asum)
20import Data.Functor (($>))
21import Data.Maybe (fromMaybe)
22import System.Random (randomRIO)
23import Prelude hiding (any, all, take, sequence, sequenceA, sequence_, takeWhile, span)
25import qualified Data.Traversable as TR
26import qualified Data.Foldable as F
27import qualified Control.Applicative as AP
28import qualified Streamly.Prelude as S
29import qualified Streamly.Internal.Data.Array.Foreign as Array
30import qualified Streamly.Internal.Data.Fold as FL
31import qualified Streamly.Internal.Data.Parser.ParserD as PR
32import qualified Streamly.Internal.Data.Producer as Producer
33import qualified Streamly.Internal.Data.Producer.Source as Source
34import qualified Streamly.Internal.Data.Stream.IsStream as IP
37import Streamly.Prelude (SerialT, MonadAsync, IsStream)
38import Streamly.Benchmark.Common
40-------------------------------------------------------------------------------
42-------------------------------------------------------------------------------
44-- XXX these can be moved to the common module
46{-# INLINE sourceUnfoldrM #-}
47sourceUnfoldrM :: (IsStream t, MonadAsync m) => Int -> Int -> t m Int
48sourceUnfoldrM value n = S.unfoldrM step n
53 else return (Just (cnt, cnt + 1))
55-- | Takes a fold method, and uses it with a default source.
56{-# INLINE benchIOSink #-}
58 :: (IsStream t, NFData b)
59 => Int -> String -> (t IO Int -> IO b) -> Benchmark
60benchIOSink value name f =
61 bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
63-- Make the input unsorted.
64{-# INLINE benchIOSinkRandom #-}
66 :: (IsStream t, NFData b)
67 => Int -> String -> (t IO Int -> IO b) -> Benchmark
68benchIOSinkRandom value name f =
69 bench name $ nfIO $ randomRIO (1,1)
71 . S.map (\x -> if even x then x + 2 else x)
72 . sourceUnfoldrM value
74-------------------------------------------------------------------------------
76-------------------------------------------------------------------------------
78{-# INLINE drainWhile #-}
79drainWhile :: MonadThrow m => (a -> Bool) -> PR.Parser m a ()
80drainWhile p = PR.takeWhile p FL.drain
82{-# INLINE sliceBeginWith #-}
83sliceBeginWith :: MonadCatch m => Int -> SerialT m Int -> m ()
84sliceBeginWith value stream = do
85 stream1 <- return . fromMaybe (S.fromPure (value + 1)) =<< S.tail stream
86 let stream2 = value `S.cons` stream1
87 IP.parseD (PR.sliceBeginWith (== value) FL.drain) stream2
89{-# INLINE takeWhile #-}
90takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
91takeWhile value = IP.parseD (drainWhile (<= value))
94takeP :: MonadThrow m => Int -> SerialT m a -> m ()
95takeP value = IP.parseD (PR.takeP value (PR.fromFold FL.drain))
97{-# INLINE takeBetween #-}
98takeBetween :: MonadCatch m => Int -> SerialT m a -> m ()
99takeBetween value = IP.parseD (PR.takeBetween 0 value FL.drain)
101{-# INLINE groupBy #-}
102groupBy :: MonadThrow m => SerialT m Int -> m ()
103groupBy = IP.parseD (PR.groupBy (<=) FL.drain)
105{-# INLINE groupByRolling #-}
106groupByRolling :: MonadThrow m => SerialT m Int -> m ()
107groupByRolling = IP.parseD (PR.groupByRolling (<=) FL.drain)
110wordBy :: MonadThrow m => Int -> SerialT m Int -> m ()
111wordBy value = IP.parseD (PR.wordBy (>= value) FL.drain)
113{-# INLINE manyWordByEven #-}
114manyWordByEven :: MonadCatch m => SerialT m Int -> m ()
116 IP.parseD (PR.many (PR.wordBy (Prelude.even) FL.drain) FL.drain)
119many :: MonadCatch m => SerialT m Int -> m Int
120many = IP.parseD (PR.many (PR.satisfy (> 0)) FL.length)
122{-# INLINE manyAlt #-}
123manyAlt :: MonadCatch m => SerialT m Int -> m Int
125 x <- IP.parseD (AP.many (PR.satisfy (> 0))) xs
126 return $ Prelude.length x
129some :: MonadCatch m => SerialT m Int -> m Int
130some = IP.parseD (PR.some (PR.satisfy (> 0)) FL.length)
132{-# INLINE someAlt #-}
133someAlt :: MonadCatch m => SerialT m Int -> m Int
135 x <- IP.parseD (AP.some (PR.satisfy (> 0))) xs
136 return $ Prelude.length x
138{-#INLINE sliceSepByP #-}
139sliceSepByP :: MonadCatch m => Int -> SerialT m Int -> m ()
140sliceSepByP value = IP.parseD (PR.sliceSepByP (>= value) (PR.fromFold FL.drain))
142{-# INLINE manyTill #-}
143manyTill :: MonadCatch m => Int -> SerialT m Int -> m Int
145 let p = PR.satisfy (> 0)
146 pcond = PR.satisfy (== value)
147 in IP.parseD (PR.manyTill FL.length p pcond)
149{-# INLINE serialWith #-}
150serialWith :: MonadThrow m
151 => Int -> SerialT m Int -> m ((), ())
155 <$> drainWhile (<= (value `div` 2))
156 <*> drainWhile (<= value)
159{-# INLINE teeAllAny #-}
160teeAllAny :: MonadThrow m
161 => Int -> SerialT m Int -> m ((), ())
165 (drainWhile (<= value))
166 (drainWhile (<= value))
169{-# INLINE teeFstAllAny #-}
170teeFstAllAny :: MonadThrow m
171 => Int -> SerialT m Int -> m ((), ())
175 (drainWhile (<= value))
176 (drainWhile (<= value))
179{-# INLINE shortestAllAny #-}
180shortestAllAny :: MonadThrow m
181 => Int -> SerialT m Int -> m ()
182shortestAllAny value =
185 (drainWhile (<= value))
186 (drainWhile (<= value))
189{-# INLINE longestAllAny #-}
190longestAllAny :: MonadCatch m
191 => Int -> SerialT m Int -> m ()
195 (drainWhile (<= value))
196 (drainWhile (<= value))
199-------------------------------------------------------------------------------
201-------------------------------------------------------------------------------
204span :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
205span value = IP.parseD (PR.span (<= (value `div` 2)) FL.drain FL.drain)
208spanBy :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
210 IP.parseD (PR.spanBy (\_ i -> i <= (value `div` 2)) FL.drain FL.drain)
212{-# INLINE spanByRolling #-}
213spanByRolling :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
215 IP.parseD (PR.spanByRolling (\_ i -> i <= value `div` 2) FL.drain FL.drain)
217-------------------------------------------------------------------------------
219-------------------------------------------------------------------------------
221{-# INLINE parseMany #-}
222parseMany :: MonadThrow m => Int -> SerialT m Int -> m ()
223parseMany value = IP.drain . IP.parseManyD (drainWhile (<= value + 1))
225{-# INLINE parseManyGroups #-}
226parseManyGroups :: MonadThrow m => Bool -> SerialT m Int -> m ()
227parseManyGroups b = IP.drain . IP.parseManyD (PR.groupBy (\_ _ -> b) FL.drain)
229{-# INLINE parseManyGroupsRolling #-}
230parseManyGroupsRolling :: MonadThrow m => Bool -> SerialT m Int -> m ()
231parseManyGroupsRolling b =
232 IP.drain . IP.parseManyD (PR.groupByRolling (\_ _ -> b) FL.drain)
234{-# INLINE parseManyGroupsRollingEither #-}
235parseManyGroupsRollingEither :: (MonadThrow m, MonadCatch m) =>
236 (Int -> Int -> Bool) -> SerialT m Int -> m ()
237parseManyGroupsRollingEither cmp =
239 . IP.parseManyD (PR.groupByRollingEither cmp FL.drain FL.drain)
241-------------------------------------------------------------------------------
242-- Parsing with unfolds
243-------------------------------------------------------------------------------
245{-# INLINE parseManyUnfoldArrays #-}
246parseManyUnfoldArrays :: Int -> [Array.Array Int] -> IO ()
247parseManyUnfoldArrays count arrays = do
248 let src = Source.source (Just (Producer.OuterLoop arrays))
249 let parser = PR.fromFold (FL.take count FL.drain)
252 $ Producer.concat Producer.fromList Array.producer
254 Producer.simplify (Source.parseManyD parser readSrc)
255 S.drain $ S.unfold streamParser src
257-------------------------------------------------------------------------------
258-- Parsers in which -fspec-constr-recursive=16 is problematic
259-------------------------------------------------------------------------------
261-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
262-- We need to fix GHC so that we can have better control over that option or do
263-- not have to rely on it.
265{-# INLINE lookAhead #-}
266lookAhead :: MonadThrow m => Int -> SerialT m Int -> m ()
268 IP.parseD (PR.lookAhead (PR.takeWhile (<= value) FL.drain) $> ())
270{-# INLINE sequenceA_ #-}
271sequenceA_ :: MonadThrow m => Int -> SerialT m Int -> m ()
273 IP.parseD (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
275-- quadratic complexity
276{-# INLINE sequenceA #-}
277sequenceA :: MonadThrow m => Int -> SerialT m Int -> m Int
278sequenceA value xs = do
279 x <- IP.parseD (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
282-- quadratic complexity
283{-# INLINE sequence #-}
284sequence :: MonadThrow m => Int -> SerialT m Int -> m Int
285sequence value xs = do
286 x <- IP.parseD (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
289{-# INLINE sequence_ #-}
290sequence_ :: MonadCatch m => Int -> SerialT m Int -> m ()
292 IP.parseD (foldr f (return ()) (replicate value (PR.takeBetween 0 1 FL.drain))) xs
297 f m k = m >>= (\_ -> k)
299-- choice using the "Alternative" instance with direct style parser type has
300-- quadratic performance complexity.
302{-# INLINE choiceAsum #-}
303choiceAsum :: MonadCatch m => Int -> SerialT m Int -> m Int
305 IP.parseD (asum (replicate value (PR.satisfy (< 0)))
306 AP.<|> PR.satisfy (> 0))
309choice :: MonadCatch m => Int -> SerialT m Int -> m Int
312 (PR.choice (replicate value (PR.satisfy (< 0))) AP.<|> PR.satisfy (> 0))
314-------------------------------------------------------------------------------
316-------------------------------------------------------------------------------
319moduleName = "Data.Parser.ParserD"
321o_1_space_serial :: Int -> [Benchmark]
322o_1_space_serial value =
323 [ benchIOSink value "takeWhile" $ takeWhile value
324 , benchIOSink value "takeP" $ takeP value
325 , benchIOSink value "takeBetween" $ takeBetween value
326 , benchIOSink value "sliceBeginWith" $ sliceBeginWith value
327 , benchIOSink value "groupBy" $ groupBy
328 , benchIOSink value "groupByRolling" $ groupByRolling
329 , benchIOSink value "wordBy" $ wordBy value
330 , benchIOSink value "serialWith" $ serialWith value
331 , benchIOSink value "many" many
332 , benchIOSink value "many (wordBy even)" $ manyWordByEven
333 , benchIOSink value "some" some
334 , benchIOSink value "sliceSepByP" $ sliceSepByP value
335 , benchIOSink value "manyTill" $ manyTill value
336 , benchIOSink value "tee (all,any)" $ teeAllAny value
337 , benchIOSink value "teeFst (all,any)" $ teeFstAllAny value
338 , benchIOSink value "shortest (all,any)" $ shortestAllAny value
339 , benchIOSink value "longest (all,any)" $ longestAllAny value
342o_1_space_serial_spanning :: Int -> [Benchmark]
343o_1_space_serial_spanning value =
344 [ benchIOSink value "span" $ span value
345 , benchIOSink value "spanBy" $ spanBy value
346 , benchIOSink value "spanByRolling" $ spanByRolling value
349o_1_space_serial_nested :: Int -> [Benchmark]
350o_1_space_serial_nested value =
351 [ benchIOSink value "parseMany" $ parseMany value
352 , benchIOSink value "parseMany groupBy (bound groups)"
353 $ parseManyGroups False
354 , benchIOSink value "parseMany groupRollingBy (bound groups)"
355 $ parseManyGroupsRolling False
356 , benchIOSink value "parseMany groupBy (1 group)" $ parseManyGroups True
357 , benchIOSink value "parseMany groupRollingBy (1 group)"
358 $ parseManyGroupsRolling True
359 , benchIOSink value "parseMany groupRollingByEither (Left)"
360 $ parseManyGroupsRollingEither (<)
361 , benchIOSink value "parseMany groupRollingByEither (Right)"
362 $ parseManyGroupsRollingEither (>)
363 , benchIOSinkRandom value "parseMany groupRollingByEither (Alternating)"
364 $ parseManyGroupsRollingEither (>)
367o_1_space_serial_unfold :: Int -> [Array.Array Int] -> [Benchmark]
368o_1_space_serial_unfold bound arrays =
369 [ bench "parseMany/Unfold/1000 arrays/1 parse"
370 $ nfIO $ parseManyUnfoldArrays bound arrays
371 , bench "parseMany/Unfold/1000 arrays/100000 parses"
372 $ nfIO $ parseManyUnfoldArrays 1 arrays
375o_n_heap_serial :: Int -> [Benchmark]
376o_n_heap_serial value =
378 -- lookahead benchmark holds the entire input till end
379 benchIOSink value "lookAhead" $ lookAhead value
381 -- These show non-linear time complexity
382 -- They accumulate the results in a list.
383 , benchIOSink value "manyAlt" manyAlt
384 , benchIOSink value "someAlt" someAlt
387-- accumulate results in a list in IO
388o_n_space_serial :: Int -> [Benchmark]
389o_n_space_serial value =
390 [ benchIOSink value "sequenceA/100" $ sequenceA (value `div` 100)
391 , benchIOSink value "sequenceA_/100" $ sequenceA_ (value `div` 100)
392 , benchIOSink value "sequence/100" $ sequence (value `div` 100)
393 , benchIOSink value "sequence_/100" $ sequence_ (value `div` 100)
394 , benchIOSink value "choice (asum)/100" $ choiceAsum (value `div` 100)
395 , benchIOSink value "choice/100" $ choice (value `div` 100)
398-------------------------------------------------------------------------------
400-------------------------------------------------------------------------------
403main = runWithCLIOptsEnv defaultStreamSize alloc allBenchmarks
407 alloc value = IP.toList $ IP.arraysOf 100 $ sourceUnfoldrM value 0
409 allBenchmarks arraysSmall value =
410 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
411 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
412 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_nested value)
413 , bgroup (o_1_space_prefix moduleName)
414 (o_1_space_serial_unfold value arraysSmall)
415 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
416 , bgroup (o_n_space_prefix moduleName) (o_n_space_serial value)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list