2-- Module : Streamly.Benchmark.Data.Parser
3-- Copyright : (c) 2020 Composewell Technologies
5-- License : BSD-3-Clause
6-- Maintainer : streamly@composewell.com
9{-# LANGUAGE FlexibleContexts #-}
10{-# LANGUAGE ScopedTypeVariables #-}
17import Control.DeepSeq (NFData(..))
18import Control.Monad.Catch (MonadCatch, MonadThrow, try, SomeException)
19import Data.Foldable (asum)
20import Data.Functor (($>))
21import Data.Maybe (fromMaybe)
22import Data.Monoid (Sum(..))
23import GHC.Magic (inline)
24import GHC.Magic (noinline)
25import System.IO (Handle)
26import System.Random (randomRIO)
28 (any, all, take, sequence, sequence_, sequenceA, takeWhile, dropWhile)
30import qualified Data.Traversable as TR
31import qualified Data.Foldable as F
32import qualified Control.Applicative as AP
33import qualified Streamly.FileSystem.Handle as Handle
34import qualified Streamly.Internal.Data.Array.Unboxed as Array
35import qualified Streamly.Internal.Data.Fold as Fold
36import qualified Streamly.Internal.Data.Parser as PR
37import qualified Streamly.Internal.Data.Stream as Stream
38import qualified Streamly.Internal.Data.Stream.IsStream as IsStream (tail)
39import qualified Streamly.Internal.Data.Producer as Producer
40import qualified Streamly.Internal.Data.Producer.Source as Source
42import Gauge hiding (env)
43import Streamly.Internal.Data.Stream (Stream)
44import Streamly.Benchmark.Common
45import Streamly.Benchmark.Common.Handle
47-------------------------------------------------------------------------------
49-------------------------------------------------------------------------------
51-- XXX these can be moved to the common module
53{-# INLINE sourceUnfoldrM #-}
54sourceUnfoldrM :: Monad m => Int -> Int -> Stream m Int
55sourceUnfoldrM value n = Stream.unfoldrM step n
60 else return (Just (cnt, cnt + 1))
62-- | Generates something like this: { { \{ \{ } }. The stream consists of
63-- three parts, the first part is contains a sequence of `{`. The second part
64-- contains a sequence pf escaped values `\{`. The third part contains a
66{-# INLINE sourceEscapedFrames #-}
72sourceEscapedFrames value n = Stream.unfoldrM step n
78 value1 = value `div` 4
81 | cnt > 4 * value1 = return Nothing
82 | cnt <= value1 = return $ Just (cbOpen, cnt + 1)
83 | cnt > 3 * value1 = return $ Just (cbClose, cnt + 1)
87 $ if (cnt - value1) `mod` 2 == 1
89 else (cbOpen, cnt + 1)
91{-# INLINE benchIOSrc #-}
94 => (Int -> Int -> Stream IO a)
97 -> (Stream IO a -> IO b)
99benchIOSrc src value name f =
100 bench name $ nfIO $ randomRIO (1,1) >>= f . src value
102-- | Takes a fold method, and uses it with a default source.
103{-# INLINE benchIOSink #-}
106 => Int -> String -> (Stream IO Int -> IO b) -> Benchmark
107benchIOSink value name f =
108 bench name $ nfIO $ randomRIO (1,1) >>= f . sourceUnfoldrM value
110-------------------------------------------------------------------------------
112-------------------------------------------------------------------------------
115one :: MonadThrow m => Int -> Stream m Int -> m (Maybe Int)
116one value = Stream.parse p
121 m <- PR.fromFold Fold.one
123 Just i -> if i >= value then pure m else p
124 Nothing -> pure Nothing
126{-# INLINE takeBetween #-}
127takeBetween :: MonadThrow m => Int -> Stream m a -> m ()
128takeBetween value = Stream.parse (PR.takeBetween 0 value Fold.drain)
131takeEQ :: MonadThrow m => Int -> Stream m a -> m ()
132takeEQ value = Stream.parse (PR.takeEQ value Fold.drain)
134{-# INLINE dropWhile #-}
135dropWhile :: MonadThrow m => Int -> Stream m Int -> m ()
136dropWhile value = Stream.parse (PR.dropWhile (<= value))
138{-# INLINE takeStartBy #-}
139takeStartBy :: MonadThrow m => Int -> Stream m Int -> m ()
140takeStartBy value stream = do
141 stream1 <- return . fromMaybe (Stream.fromPure (value + 1)) =<< IsStream.tail stream
142 let stream2 = value `Stream.cons` stream1
143 Stream.parse (PR.takeStartBy (== value) Fold.drain) stream2
145takeFramedByEsc_ :: MonadThrow m => Int -> Stream m Char -> m ()
146takeFramedByEsc_ _ = Stream.parse parser
154 parser = PR.takeFramedByEsc_ isEsc isBegin isEnd Fold.drain
156{-# INLINE takeWhile #-}
157takeWhile :: MonadThrow m => Int -> Stream m Int -> m ()
158takeWhile value = Stream.parse (PR.takeWhile (<= value) Fold.drain)
160takeWhileP :: MonadThrow m => Int -> Stream m Int -> m ()
162 Stream.parse (PR.takeWhileP (<= value) (PR.takeWhile (<= value - 1) Fold.drain))
165takeP :: MonadThrow m => Int -> Stream m a -> m ()
166takeP value = Stream.parse (PR.takeP value (PR.fromFold Fold.drain))
168{-# INLINE groupBy #-}
169groupBy :: MonadThrow m => Stream m Int -> m ()
170groupBy = Stream.parse (PR.groupBy (<=) Fold.drain)
172{-# INLINE groupByRolling #-}
173groupByRolling :: MonadThrow m => Stream m Int -> m ()
174groupByRolling = Stream.parse (PR.groupByRolling (<=) Fold.drain)
177wordBy :: MonadThrow m => Int -> Stream m Int -> m ()
178wordBy value = Stream.parse (PR.wordBy (>= value) Fold.drain)
180{-# INLINE sepByWords #-}
181sepByWords :: MonadThrow m => Int -> Stream m Int -> m ()
182sepByWords _ = Stream.parse (wrds even Fold.drain)
184 wrds p f = PR.sepBy f (PR.takeWhile (not . p) Fold.drain) (PR.dropWhile p)
186{-# INLINE deintercalate #-}
187deintercalate :: MonadThrow m => Int -> Stream m Int -> m ()
188deintercalate _ = Stream.parse (partition even)
194 Fold.drain (PR.takeWhile (not . p) Fold.sum) (PR.takeWhile p Fold.sum)
196{-# INLINE manyWordByEven #-}
197manyWordByEven :: MonadThrow m => Stream m Int -> m ()
198manyWordByEven = Stream.parse (PR.many (PR.wordBy even Fold.drain) Fold.drain)
201many :: MonadThrow m => Stream m Int -> m Int
202many = Stream.parse (PR.many (PR.satisfy (> 0)) Fold.length)
204{-# INLINE manyAlt #-}
205manyAlt :: MonadThrow m => Stream m Int -> m Int
207 x <- Stream.parse (AP.many (PR.satisfy (> 0))) xs
208 return $ Prelude.length x
211some :: MonadThrow m => Stream m Int -> m Int
212some = Stream.parse (PR.some (PR.satisfy (> 0)) Fold.length)
214{-# INLINE someAlt #-}
215someAlt :: MonadThrow m => Stream m Int -> m Int
217 x <- Stream.parse (AP.some (PR.satisfy (> 0))) xs
218 return $ Prelude.length x
220{-# INLINE manyTill #-}
221manyTill :: MonadThrow m => Int -> Stream m Int -> m Int
223 Stream.parse (PR.manyTill (PR.satisfy (> 0)) (PR.satisfy (== value)) Fold.length)
225{-# INLINE splitAp #-}
226splitAp :: MonadThrow m
227 => Int -> Stream m Int -> m ((), ())
231 <$> PR.dropWhile (<= (value `div` 2))
232 <*> PR.dropWhile (<= value)
235{-# INLINE splitApBefore #-}
236splitApBefore :: MonadThrow m
237 => Int -> Stream m Int -> m ()
240 ( PR.dropWhile (<= (value `div` 2))
241 *> PR.dropWhile (<= value)
244{-# INLINE splitApAfter #-}
245splitApAfter :: MonadThrow m
246 => Int -> Stream m Int -> m ()
249 ( PR.dropWhile (<= (value `div` 2))
250 <* PR.dropWhile (<= value)
253{-# INLINE serialWith #-}
254serialWith :: MonadThrow m
255 => Int -> Stream m Int -> m ((), ())
259 (PR.dropWhile (<= (value `div` 2)))
260 (PR.dropWhile (<= value))
264split_ :: MonadThrow m
265 => Int -> Stream m Int -> m ()
269 (PR.dropWhile (<= (value `div` 2)))
270 (PR.dropWhile (<= value))
273{-# INLINE takeEndBy_ #-}
274takeEndBy_ :: MonadThrow m
275 => Int -> Stream m Int -> m()
276takeEndBy_ value = Stream.parse (PR.takeEndBy_ (>= value) (PR.fromFold Fold.drain))
279{-# INLINE teeAllAny #-}
280teeAllAny :: MonadThrow m
281 => Int -> Stream m Int -> m ((), ())
285 (PR.dropWhile (<= value))
286 (PR.dropWhile (<= value))
289{-# INLINE teeFstAllAny #-}
290teeFstAllAny :: MonadThrow m
291 => Int -> Stream m Int -> m ((), ())
295 (PR.dropWhile (<= value))
296 (PR.dropWhile (<= value))
299{-# INLINE shortestAllAny #-}
300shortestAllAny :: MonadThrow m
301 => Int -> Stream m Int -> m ()
302shortestAllAny value =
305 (PR.dropWhile (<= value))
306 (PR.dropWhile (<= value))
309{-# INLINE longestAllAny #-}
310longestAllAny :: MonadThrow m
311 => Int -> Stream m Int -> m ()
315 (PR.dropWhile (<= value))
316 (PR.dropWhile (<= value))
320parseManyChunksOfSum :: Int -> Handle -> IO Int
321parseManyChunksOfSum n inh =
322 Stream.fold Fold.length
324 (PR.fromFold $ Fold.take n Fold.sum)
325 (Stream.unfold Handle.read inh)
327-------------------------------------------------------------------------------
328-- Parsing with unfolds
329-------------------------------------------------------------------------------
331{-# INLINE parseManyUnfoldArrays #-}
332parseManyUnfoldArrays :: Int -> [Array.Array Int] -> IO ()
333parseManyUnfoldArrays count arrays = do
334 let src = Source.source (Just (Producer.OuterLoop arrays))
335 let parser = PR.fromFold (Fold.take count Fold.drain)
338 $ Producer.concat Producer.fromList Array.producer
340 Producer.simplify (Source.parseMany parser readSrc)
341 Stream.fold Fold.drain $ Stream.unfold streamParser src
343-------------------------------------------------------------------------------
344-- Parsers in which -fspec-constr-recursive=16 is problematic
345-------------------------------------------------------------------------------
347-- XXX -fspec-constr-recursive=16 makes GHC go beserk when compiling these.
348-- We need to fix GHC so that we can have better control over that option or do
349-- not have to rely on it.
351{-# INLINE lookAhead #-}
352lookAhead :: MonadThrow m => Int -> Stream m Int -> m ()
354 Stream.parse (PR.lookAhead (PR.takeWhile (<= value) Fold.drain) $> ())
356{-# INLINE sequenceA #-}
357sequenceA :: MonadThrow m => Int -> Stream m Int -> m Int
358sequenceA value xs = do
359 x <- Stream.parse (TR.sequenceA (replicate value (PR.satisfy (> 0)))) xs
362{-# INLINE sequenceA_ #-}
363sequenceA_ :: MonadThrow m => Int -> Stream m Int -> m ()
365 Stream.parse (F.sequenceA_ $ replicate value (PR.satisfy (> 0)))
367{-# INLINE sequence #-}
368sequence :: MonadThrow m => Int -> Stream m Int -> m Int
369sequence value xs = do
370 x <- Stream.parse (TR.sequence (replicate value (PR.satisfy (> 0)))) xs
373{-# INLINE sequence_ #-}
374sequence_ :: MonadThrow m => Int -> Stream m Int -> m ()
376 Stream.parse (F.sequence_ $ replicate value (PR.satisfy (> 0)))
378{-# INLINE choiceAsum #-}
379choiceAsum :: MonadThrow m => Int -> Stream m Int -> m Int
381 Stream.parse (asum (replicate value (PR.satisfy (< 0)))
382 AP.<|> PR.satisfy (> 0))
386choice :: MonadThrow m => Int -> Stream m Int -> m Int
389 (PR.choice (replicate value (PR.satisfy (< 0))) AP.<|> PR.satisfy (> 0))
392-------------------------------------------------------------------------------
393-- Stream transformation
394-------------------------------------------------------------------------------
396{-# INLINE parseMany #-}
397parseMany :: MonadThrow m => Int -> Stream m Int -> m ()
399 Stream.fold Fold.drain
401 . Stream.parseMany (PR.fromFold $ Fold.take n Fold.mconcat)
404{-# INLINE parseIterate #-}
405parseIterate :: MonadThrow m => Int -> Stream m Int -> m ()
407 Stream.fold Fold.drain
409 . Stream.parseIterate (PR.fromFold . Fold.take n . Fold.sconcat) (Sum 0)
412{-# INLINE parseBreak #-}
413parseBreak :: MonadCatch m => Stream m Int -> m ()
415 r <- try $ Stream.parseBreak PR.one s
417 Left (_ :: SomeException) -> return ()
418 Right (_, s1) -> parseBreak s1
420{-# INLINE concatSequence #-}
421concatSequence :: MonadThrow m => Stream m Int -> m ()
422concatSequence = Stream.parse $ PR.concatSequence Fold.drain $ Stream.repeat PR.one
424{-# INLINE parseManyGroupBy #-}
425parseManyGroupBy :: MonadThrow m => (Int -> Int -> Bool) -> Stream m Int -> m ()
426parseManyGroupBy cmp =
427 Stream.fold Fold.drain . Stream.parseMany (PR.groupBy cmp Fold.drain)
429-------------------------------------------------------------------------------
431-------------------------------------------------------------------------------
434moduleName = "Data.Parser"
436o_1_space_serial :: Int -> [Benchmark]
437o_1_space_serial value =
438 [ benchIOSink value "one (fold)" $ one value
439 , benchIOSink value "takeBetween" $ takeBetween value
440 , benchIOSink value "takeEQ" $ takeEQ value
441 , benchIOSink value "takeWhile" $ takeWhile value
442 , benchIOSink value "takeWhileP" $ takeWhileP value
443 , benchIOSink value "takeP" $ takeP value
444 , benchIOSink value "dropWhile" $ dropWhile value
445 , benchIOSink value "takeStartBy" $ takeStartBy value
446 , benchIOSrc sourceEscapedFrames value "takeFramedByEsc_"
447 $ takeFramedByEsc_ value
448 , benchIOSink value "groupBy" $ groupBy
449 , benchIOSink value "groupByRolling" $ groupByRolling
450 , benchIOSink value "wordBy" $ wordBy value
451 , benchIOSink value "sepBy (words)" $ sepByWords value
452 , benchIOSink value "deintercalate" $ deintercalate value
453 , benchIOSink value "splitAp" $ splitAp value
454 , benchIOSink value "splitApBefore" $ splitApBefore value
455 , benchIOSink value "splitApAfter" $ splitApAfter value
456 , benchIOSink value "serialWith" $ serialWith value
457 , benchIOSink value "takeEndBy_" $ takeEndBy_ value
458 , benchIOSink value "many" many
459 , benchIOSink value "many (wordBy even)" $ manyWordByEven
460 , benchIOSink value "some" some
461 , benchIOSink value "manyTill" $ manyTill value
463 , benchIOSink value "tee" $ teeAllAny value
464 , benchIOSink value "teeFst" $ teeFstAllAny value
465 , benchIOSink value "shortest" $ shortestAllAny value
466 , benchIOSink value "longest" $ longestAllAny value
468 , benchIOSink value "parseBreak (recursive)" parseBreak
469 , benchIOSink value "parseMany (take 1)" (parseMany 1)
470 , benchIOSink value "parseMany (take all)" (parseMany value)
471 , benchIOSink value "parseIterate (take 1)" (parseIterate 1)
472 , benchIOSink value "parseIterate (take all)" (parseIterate value)
473 , benchIOSink value "concatSequence" concatSequence
474 , benchIOSink value "parseMany (groupBy (<))" (parseManyGroupBy (<))
475 , benchIOSink value "parseMany (groupBy (==))" (parseManyGroupBy (==))
478o_1_space_filesystem :: BenchEnv -> [Benchmark]
479o_1_space_filesystem env =
480 [ mkBench ("S.parseMany (Fold.take " ++ show (bigSize env) ++ " Fold.sum)") env
481 $ \inh _ -> noinline parseManyChunksOfSum (bigSize env) inh
482 , mkBench "S.parseMany (Fold.take 1 Fold.sum)" env
483 $ \inh _ -> inline parseManyChunksOfSum 1 inh
486o_1_space_serial_unfold :: Int -> [Array.Array Int] -> [Benchmark]
487o_1_space_serial_unfold bound arrays =
488 [ bench "parseMany/Unfold/1000 arrays/take all"
489 $ nfIO $ parseManyUnfoldArrays bound arrays
490 , bench "parseMany/Unfold/1000 arrays/take 1"
491 $ nfIO $ parseManyUnfoldArrays 1 arrays
494o_n_heap_serial :: Int -> [Benchmark]
495o_n_heap_serial value =
497 -- lookahead benchmark holds the entire input till end
498 benchIOSink value "lookAhead" $ lookAhead value
500 -- accumulates the results in a list
501 , benchIOSink value "sequence" $ sequence value
502 , benchIOSink value "sequenceA" $ sequenceA value
504 -- XXX why should this take O(n) heap, it discards the results?
505 , benchIOSink value "sequence_" $ sequence_ value
506 , benchIOSink value "sequenceA_" $ sequenceA_ value
507 -- non-linear time complexity (parserD)
508 , benchIOSink value "split_" $ split_ value
509 -- XXX why O(n) heap?
510 , benchIOSink value "choice (asum)" $ choiceAsum value
511 -- , benchIOSink value "choice" $ choice value
513 -- These show non-linear time complexity.
514 -- They accumulate the results in a list.
515 , benchIOSink value "manyAlt" manyAlt
516 , benchIOSink value "someAlt" someAlt
519-------------------------------------------------------------------------------
521-------------------------------------------------------------------------------
525 env <- mkHandleBenchEnv
526 runWithCLIOptsEnv defaultStreamSize alloc (allBenchmarks env)
530 alloc value = Stream.fold Fold.toList $ Stream.arraysOf 100 $ sourceUnfoldrM value 0
532 allBenchmarks env arrays value =
533 [ bgroup (o_1_space_prefix moduleName) (o_1_space_serial value)
535 (o_1_space_prefix moduleName ++ "/filesystem")
536 (o_1_space_filesystem env)
537 , bgroup (o_1_space_prefix moduleName)
538 (o_1_space_serial_unfold value arrays)
539 , bgroup (o_n_heap_prefix moduleName) (o_n_heap_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