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.Fold as FL
30import qualified Streamly.Internal.Data.Parser.ParserD as PR
31import qualified Streamly.Internal.Data.Stream.IsStream as IP
34import Streamly.Prelude (SerialT, MonadAsync, IsStream)
35import Streamly.Benchmark.Common
37-------------------------------------------------------------------------------
39-------------------------------------------------------------------------------
41-- XXX these can be moved to the common module
43{-# INLINE sourceUnfoldrM #-}
44sourceUnfoldrM :: (IsStream t, MonadAsync m) => Int -> Int -> t m Int
45sourceUnfoldrM value n = S.unfoldrM step n
50 else return (Just (cnt, cnt + 1))
52-- | Takes a fold method, and uses it with a default source.
53{-# INLINE benchIOSink #-}
55 :: (IsStream t, NFData b)
56 => Int -> String -> (t IO Int -> IO b) -> Benchmark
57benchIOSink value name f =
58 bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
60-------------------------------------------------------------------------------
62-------------------------------------------------------------------------------
64{-# INLINE drainWhile #-}
65drainWhile :: MonadThrow m => (a -> Bool) -> PR.Parser m a ()
66drainWhile p = PR.takeWhile p FL.drain
68{-# INLINE sliceBeginWith #-}
69sliceBeginWith :: MonadCatch m => Int -> SerialT m Int -> m ()
70sliceBeginWith value stream = do
71 stream1 <- return . fromMaybe (S.yield (value + 1)) =<< S.tail stream
72 let stream2 = value `S.cons` stream1
73 IP.parseD (PR.sliceBeginWith (== value) FL.drain) stream2
75{-# INLINE takeWhile #-}
76takeWhile :: MonadThrow m => Int -> SerialT m Int -> m ()
77takeWhile value = IP.parseD (drainWhile (<= value))
80groupBy :: MonadThrow m => SerialT m Int -> m ()
81groupBy = IP.parseD (PR.groupBy (<=) FL.drain)
83{-# INLINE groupByRolling #-}
84groupByRolling :: MonadThrow m => SerialT m Int -> m ()
85groupByRolling = IP.parseD (PR.groupByRolling (<=) FL.drain)
88wordBy :: MonadThrow m => Int -> SerialT m Int -> m ()
89wordBy value = IP.parseD (PR.wordBy (>= value) FL.drain)
91{-# INLINE manyWordByEven #-}
92manyWordByEven :: MonadCatch m => SerialT m Int -> m ()
94 IP.parseD (PR.many FL.drain (PR.wordBy (Prelude.even) FL.drain))
97many :: MonadCatch m => SerialT m Int -> m Int
98many = IP.parseD (PR.many FL.length (PR.satisfy (> 0)))
100{-# INLINE manyAlt #-}
101manyAlt :: MonadCatch m => SerialT m Int -> m Int
103 x <- IP.parseD (AP.many (PR.satisfy (> 0))) xs
104 return $ Prelude.length x
107some :: MonadCatch m => SerialT m Int -> m Int
108some = IP.parseD (PR.some FL.length (PR.satisfy (> 0)))
110{-# INLINE someAlt #-}
111someAlt :: MonadCatch m => SerialT m Int -> m Int
113 x <- IP.parseD (AP.some (PR.satisfy (> 0))) xs
114 return $ Prelude.length x
116{-#INLINE sliceSepByP #-}
117sliceSepByP :: MonadCatch m => Int -> SerialT m Int -> m ()
118sliceSepByP value = IP.parseD (PR.sliceSepByP (>= value) (PR.fromFold FL.drain))
120{-# INLINE manyTill #-}
121manyTill :: MonadCatch m => Int -> SerialT m Int -> m Int
123 let p = PR.satisfy (> 0)
124 pcond = PR.satisfy (== value)
125 in IP.parseD (PR.manyTill FL.length p pcond)
127{-# INLINE splitWith #-}
128splitWith :: MonadThrow m
129 => Int -> SerialT m Int -> m ((), ())
133 <$> drainWhile (<= (value `div` 2))
134 <*> drainWhile (<= value)
137{-# INLINE teeAllAny #-}
138teeAllAny :: MonadThrow m
139 => Int -> SerialT m Int -> m ((), ())
143 (drainWhile (<= value))
144 (drainWhile (<= value))
147{-# INLINE teeFstAllAny #-}
148teeFstAllAny :: MonadThrow m
149 => Int -> SerialT m Int -> m ((), ())
153 (drainWhile (<= value))
154 (drainWhile (<= value))
157{-# INLINE shortestAllAny #-}
158shortestAllAny :: MonadThrow m
159 => Int -> SerialT m Int -> m ()
160shortestAllAny value =
163 (drainWhile (<= value))
164 (drainWhile (<= value))
167{-# INLINE longestAllAny #-}
168longestAllAny :: MonadCatch m
169 => Int -> SerialT m Int -> m ()
173 (drainWhile (<= value))
174 (drainWhile (<= value))
177-------------------------------------------------------------------------------
179-------------------------------------------------------------------------------
182span :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
183span value = IP.parseD (PR.span (<= (value `div` 2)) FL.drain FL.drain)
186spanBy :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
188 IP.parseD (PR.spanBy (\_ i -> i <= (value `div` 2)) FL.drain FL.drain)
190{-# INLINE spanByRolling #-}
191spanByRolling :: MonadThrow m => Int -> SerialT m Int -> m ((), ())
193 IP.parseD (PR.spanByRolling (\_ i -> i <= value `div` 2) FL.drain FL.drain)
195-------------------------------------------------------------------------------
197-------------------------------------------------------------------------------
199{-# INLINE parseMany #-}
200parseMany :: MonadThrow m => Int -> SerialT m Int -> m ()
201parseMany value = IP.drain . IP.parseManyD (drainWhile (<= value + 1))
203{-# INLINE parseManyGroups #-}
204parseManyGroups :: MonadThrow m => Bool -> SerialT m Int -> m ()
205parseManyGroups b = IP.drain . IP.parseManyD (PR.groupBy (\_ _ -> b) FL.drain)
207{-# INLINE parseManyGroupsRolling #-}
208parseManyGroupsRolling :: MonadThrow m => Bool -> SerialT m Int -> m ()
209parseManyGroupsRolling b =
210 IP.drain . IP.parseManyD (PR.groupByRolling (\_ _ -> b) FL.drain)
212-------------------------------------------------------------------------------
213-- Parsers in which -fspec-constr-recursive=16 is problematic
214-------------------------------------------------------------------------------
216-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
217-- We need to fix GHC so that we can have better control over that option or do
218-- not have to rely on it.
220{-# INLINE lookAhead #-}
221lookAhead :: MonadThrow m => Int -> SerialT m Int -> m ()
223 IP.parseD (PR.lookAhead (PR.takeWhile (<= value) FL.drain) $> ())
225{-# INLINE sequenceA_ #-}
226sequenceA_ :: MonadThrow m => Int -> SerialT m Int -> m ()
228 IP.parseD (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
230-- quadratic complexity
231{-# INLINE sequenceA #-}
232sequenceA :: MonadThrow m => Int -> SerialT m Int -> m Int
233sequenceA value xs = do
234 x <- IP.parseD (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
237-- quadratic complexity
238{-# INLINE sequence #-}
239sequence :: MonadThrow m => Int -> SerialT m Int -> m Int
240sequence value xs = do
241 x <- IP.parseD (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
244{-# INLINE sequence_ #-}
245sequence_ :: MonadCatch m => Int -> SerialT m Int -> m ()
247 IP.parseD (foldr f (return ()) (replicate value (PR.takeBetween 0 1 FL.drain))) xs
252 f m k = m >>= (\_ -> k)
254-- choice using the "Alternative" instance with direct style parser type has
255-- quadratic performance complexity.
258choice :: MonadCatch m => Int -> SerialT m Int -> m Int
260 IP.parseD (asum (replicate value (PR.satisfy (< 0)))
261 AP.<|> PR.satisfy (> 0))
263-------------------------------------------------------------------------------
265-------------------------------------------------------------------------------
268moduleName = "Data.Parser.ParserD"
270o_1_space_serial :: Int -> [Benchmark]
271o_1_space_serial value =
272 [ benchIOSink value "takeWhile" $ takeWhile value
273 , benchIOSink value "sliceBeginWith" $ sliceBeginWith value
274 , benchIOSink value "groupBy" $ groupBy
275 , benchIOSink value "groupByRolling" $ groupByRolling
276 , benchIOSink value "wordBy" $ wordBy value
277 , benchIOSink value "splitWith" $ splitWith value
278 , benchIOSink value "many" many
279 , benchIOSink value "many (wordBy even)" $ manyWordByEven
280 , benchIOSink value "some" some
281 , benchIOSink value "sliceSepByP" $ sliceSepByP value
282 , benchIOSink value "manyTill" $ manyTill value
283 , benchIOSink value "tee (all,any)" $ teeAllAny value
284 , benchIOSink value "teeFst (all,any)" $ teeFstAllAny value
285 , benchIOSink value "shortest (all,any)" $ shortestAllAny value
286 , benchIOSink value "longest (all,any)" $ longestAllAny value
289o_1_space_serial_spanning :: Int -> [Benchmark]
290o_1_space_serial_spanning value =
291 [ benchIOSink value "span" $ span value
292 , benchIOSink value "spanBy" $ spanBy value
293 , benchIOSink value "spanByRolling" $ spanByRolling value
296o_1_space_serial_nested :: Int -> [Benchmark]
297o_1_space_serial_nested value =
298 [ benchIOSink value "parseMany" $ parseMany value
299 , benchIOSink value "parseMany groupBy (bound groups)"
300 $ (parseManyGroups False)
301 , benchIOSink value "parseMany groupRollingBy (bound groups)"
302 $ (parseManyGroupsRolling False)
303 , benchIOSink value "parseMany groupBy (1 group)" $ (parseManyGroups True)
304 , benchIOSink value "parseMany groupRollingBy (1 group)"
305 $ (parseManyGroupsRolling True)
308o_n_heap_serial :: Int -> [Benchmark]
309o_n_heap_serial value =
311 -- lookahead benchmark holds the entire input till end
312 benchIOSink value "lookAhead" $ lookAhead value
314 -- These show non-linear time complexity
315 -- They accumulate the results in a list.
316 , benchIOSink value "manyAlt" manyAlt
317 , benchIOSink value "someAlt" someAlt
320-- accumulate results in a list in IO
321o_n_space_serial :: Int -> [Benchmark]
322o_n_space_serial value =
323 [ benchIOSink value "sequenceA/100" $ sequenceA (value `div` 100)
324 , benchIOSink value "sequenceA_/100" $ sequenceA_ (value `div` 100)
325 , benchIOSink value "sequence/100" $ sequence (value `div` 100)
326 , benchIOSink value "sequence_/100" $ sequence_ (value `div` 100)
327 , benchIOSink value "choice/100" $ choice (value `div` 100)
330-------------------------------------------------------------------------------
332-------------------------------------------------------------------------------
336 (value, cfg, benches) <- parseCLIOpts defaultStreamSize
337 value `seq` runMode (mode cfg) cfg benches (allBenchmarks value)
341 allBenchmarks value =
342 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
343 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_spanning value)
344 , bgroup (o_1_space_prefix moduleName) (o_1_space_serial_nested value)
345 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_serial value)
346 , 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