packages feed

streaming-benchmarks (empty) → 0.1.0

raw patch · 10 files changed

+1165/−0 lines, 10 filesdep +Chartdep +Chart-diagramsdep +base

Dependencies added: Chart, Chart-diagrams, base, bytestring, conduit, csv, deepseq, directory, gauge, list-t, list-transformer, logict, machines, mtl, pipes, random, split, streaming, streamly, text, transformers, typed-process, vector

Files

+ Benchmarks.hs view
@@ -0,0 +1,449 @@+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE RankNTypes           #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++module Main (main) where++import Control.DeepSeq (NFData)+import Control.Monad.IO.Class (MonadIO(liftIO))+import Control.Monad.Trans.Class (lift)+import Gauge+import Data.Foldable (msum)+import Data.Function ((&))+--import System.Random (randomIO)+import System.Random (randomRIO)++import qualified Streamly          as A+import qualified Streamly.Prelude  as A+import qualified Data.Conduit      as C+import qualified Data.Conduit.Combinators as CC+import qualified Data.Conduit.List as C+import qualified List.Transformer  as L+import qualified ListT             as LT+import qualified Control.Monad.Logic as LG+import qualified Data.Machine      as M+import qualified Pipes             as P+import qualified Pipes.Prelude     as P+import qualified Streaming.Prelude as S+import qualified Data.Vector.Fusion.Stream.Monadic as V+-- import qualified Conduit.Simple    as SC+--++-- Orphan instance to use nfIO on streaming+instance (NFData a, NFData b) => NFData (S.Of a b)++getRandom :: MonadIO m => m Int+getRandom =  liftIO $ randomRIO (1,1000)++value, maxValue :: Int+value = 1000000+maxValue = value + 1000++-------------------------------------------------------------------------------+-- Streamly+-------------------------------------------------------------------------------++sourceA :: MonadIO m => A.StreamT m Int+sourceA = getRandom >>= \v -> A.each [v..v+value]++-- Note, streamly provides two different ways to compose, i.e. category style+-- composition and monadic compostion.++-- Category composition+runIOA :: A.StreamT IO Int -> (A.StreamT IO Int -> A.StreamT IO Int) -> IO ()+runIOA s t = A.runStreamT $ s & t++{-+-- Monadic composition+runIOA_M :: A.StreamT IO Int -> (Int -> A.StreamT IO Int) -> IO ()+runIOA_M s t = A.runStreamT $ s >>= t+-}++-------------------------------------------------------------------------------+-- streaming+-------------------------------------------------------------------------------++sourceS :: MonadIO m => S.Stream (S.Of Int) m ()+sourceS = getRandom >>= \v -> S.each [v..v+value]++runIOS :: S.Stream (S.Of Int) IO ()+    -> (S.Stream (S.Of Int) IO () -> S.Stream (S.Of Int) IO ()) -> IO ()+runIOS s t = s & t & S.mapM_ (\_ -> return ())++-------------------------------------------------------------------------------+-- simple-conduit+-------------------------------------------------------------------------------++{-+sourceSC :: MonadIO m => SC.Source m Int+sourceSC = getRandom >>= \v -> SC.enumFromToC v (v + value)++runIOSC :: SC.Source IO Int -> SC.Conduit Int IO a -> IO ()+runIOSC s t = s SC.$= t SC.$$ SC.mapM_C (\_ -> return ())+-}++-------------------------------------------------------------------------------+-- conduit+-------------------------------------------------------------------------------++sourceC :: MonadIO m => C.ConduitT () Int m ()+sourceC = getRandom >>= \v -> C.enumFromTo v (v + value)++runIOC :: C.ConduitT () Int IO () -> C.ConduitT Int a IO () -> IO ()+runIOC s t = C.runConduit $ s C..| t C..| C.mapM_ (\_ -> return ())++-------------------------------------------------------------------------------+-- pipes+-------------------------------------------------------------------------------++sourceP :: MonadIO m => P.Producer' Int m ()+sourceP = getRandom >>= \v -> P.each [v..v+value]++runIOP :: P.Producer' Int IO () -> P.Proxy () Int () a IO () -> IO ()+runIOP s t = P.runEffect $ s P.>-> t P.>-> P.mapM_ (\_ -> return ())++-------------------------------------------------------------------------------+-- machines+-------------------------------------------------------------------------------++sourceM :: Monad m => Int -> M.SourceT m Int+sourceM v = M.enumerateFromTo v (v + value)++runIOM :: M.SourceT IO Int -> M.ProcessT IO Int o -> IO ()+runIOM s t = M.runT_ (s M.~> t)++-------------------------------------------------------------------------------+-- list-transformer+-------------------------------------------------------------------------------++sourceL :: MonadIO m => L.ListT m Int+sourceL = getRandom >>= \v -> L.select [v..v+value]++runIOL :: L.ListT IO Int -> (Int -> L.ListT IO Int) -> IO ()+runIOL s t = L.runListT (s >>= t)++-------------------------------------------------------------------------------+-- list-t+-------------------------------------------------------------------------------++sourceLT :: MonadIO m => LT.ListT m Int+sourceLT = getRandom >>= \v -> LT.fromFoldable [v..v+value]++runIOLT :: LT.ListT IO Int -> (Int -> LT.ListT IO Int) -> IO ()+runIOLT s t = LT.traverse_ (\_ -> return ()) (s >>= t)++-------------------------------------------------------------------------------+-- logict+-------------------------------------------------------------------------------++sourceLG :: Int -> LG.LogicT m Int+sourceLG v = msum $ map return [v..v+value]++runIOLG :: LG.LogicT IO Int -> (Int -> LG.LogicT IO Int) -> IO ()+runIOLG s t = LG.observeAllT (s >>= t) >> return ()++-------------------------------------------------------------------------------+-- vector+-------------------------------------------------------------------------------++sourceV :: Monad m => Int -> V.Stream m Int+sourceV v = V.fromList [v..v+value]++runIOV :: V.Stream IO Int -> (V.Stream IO Int -> V.Stream IO Int) -> IO ()+runIOV s t = s & t & V.mapM_ (\_ -> return ())++-------------------------------------------------------------------------------+-- Benchmarks+-------------------------------------------------------------------------------++main :: IO ()+main =+  defaultMain+  [ bgroup "elimination"+    [+      bgroup "toNull"+        [+          bench "conduit"          $ nfIO $ C.runConduit $ sourceC C..| C.mapM_ (\_ -> return ())+        , bench "pipes"            $ nfIO $ P.runEffect $ sourceP P.>-> P.mapM_ (\_ -> return ())+        , bench "machines"         $ nfIO $ getRandom >>= \v -> M.runT_ (sourceM v)+        , bench "streaming"        $ nfIO $ runIOS sourceS id+        , bench "streamly"         $ nfIO $ runIOA sourceA id+        -- , bench "simple-conduit"   $ nfIO $ sourceSC SC.$$ SC.mapM_C (\_ -> return ())+        , bench "logict"           $ nfIO $ getRandom >>= \v -> LG.observeAllT (sourceLG v) >> return ()+        , bench "list-t"           $ nfIO $ LT.traverse_ (\_ -> return ()) sourceLT+        , bench "list-transformer" $ nfIO $ L.runListT sourceL+        , bench "vector"           $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) id+        ]+    , bgroup "toList"+          [+            bench "conduit"   $ nfIO $ C.runConduit $ sourceC C..| CC.sinkList+          , bench "pipes"     $ nfIO $ P.toListM sourceP+          , bench "machines"  $ nfIO $ getRandom >>= \v -> M.runT (sourceM v)+          , bench "streaming" $ nfIO $ S.toList sourceS+          , bench "streamly"  $ nfIO $ A.toList sourceA+          -- , bench "simple-conduit" $ nfIO $ sourceSC SC.$$ SC.sinkList+          , bench "logict"         $ nfIO $ getRandom >>= \v -> LG.observeAllT (sourceLG v) >> return ()+          , bench "list-t"         $ nfIO $ LT.toList sourceLT+          -- , bench "list-transformer" $ nfIO $ toList sourceL+          , bench "vector"         $ nfIO $ getRandom >>= \v -> V.toList (sourceV v)+          ]+    , bgroup "fold"+        [ bench "streamly"  $ nfIO   $ A.foldl (+) 0 id sourceA+        , bench "streaming" $ nfIO $ S.fold (+) 0 id sourceS+        , bench "conduit"   $ nfIO   $ C.runConduit $ sourceC C..| (C.fold (+) 0)+        , bench "pipes"     $ nfIO   $ P.fold (+) 0 id sourceP+        , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.fold (+) 0)+        , bench "list-transformer" $ nfIO $ L.fold (+) 0 id sourceL+        , bench "vector"    $ nfIO $ getRandom >>= \v -> V.foldl' (+) 0 (sourceV v)+        ]+    , bgroup "scan"+        [ bench "conduit" $ nfIO $ runIOC sourceC (CC.scanl (+) 0)+        , bench "pipes" $ nfIO $ runIOP sourceP (P.scan (+) 0 id)+        , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.scan (+) 0)+        , bench "streaming" $ nfIO $ runIOS sourceS (S.scan (+) 0 id)+        , bench "streamly" $ nfIO $ runIOA sourceA (A.scan (+) 0 id)+        , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.prescanl' (+) 0)+        ]+    , bgroup "last"+          [ bench "pipes" $ nfIO $ P.last sourceP+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.final)+          , bench "streaming" $ nfIO $ S.last sourceS+          , bench "streamly"  $ nfIO $ A.last sourceA+          , bench "conduit"  $ nfIO $ C.runConduit $ sourceC C..| CC.last+          , bench "vector"  $ nfIO $ getRandom >>= \v -> V.last (sourceV v)+          ]+    , bgroup "concat"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.map (replicate 3) C..| C.concat)+          , bench "pipes" $ nfIO $ runIOP sourceP (P.map (replicate 3) P.>-> P.concat)+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.mapping (replicate 3) M.~> M.asParts)+          -- XXX This hangs indefinitely+          -- , bench "streaming" $ nfIO $ runIOS sourceS (S.concat . S.map (replicate 3))+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.concatMap (V.fromList . replicate 3))+          ]+    ]+    , bgroup "transformation"+        [ bgroup "map"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.map (+1))+          , bench "pipes" $ nfIO $ runIOP sourceP (P.map (+1))+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.mapping (+1))+          , bench "streaming" $ nfIO $ runIOS sourceS (S.map (+1))+          , bench "streamly" $ nfIO $ runIOA sourceA (fmap (+1))+          -- , bench "simple-conduit" $ nfIO $ runIOSC sourceSC (SC.mapC (+1))+          , bench "list-transformer" $ nfIO $ runIOL sourceL (lift . return . (+1))+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.map (+1))+          ]+        , bgroup "mapM"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.mapM return)+          , bench "pipes" $ nfIO $ runIOP sourceP (P.mapM return)+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.autoM return)+          , bench "streaming" $ nfIO $ runIOS sourceS (S.mapM return)+          , bench "streamly" $ nfIO $ runIOA sourceA (A.mapM return)+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.mapM return)+          ]+        ]+    , bgroup "filtering"+        [ bgroup "filter-even"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.filter even)+          , bench "pipes" $ nfIO $ runIOP sourceP (P.filter even)+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.filtered even)+          , bench "streaming" $ nfIO $ runIOS sourceS (S.filter even)+          , bench "streamly" $ nfIO $ runIOA sourceA (A.filter even)+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.filter even)+          ]+        , bgroup "filter-all-out"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.filter (> maxValue))+          , bench "pipes" $ nfIO $ runIOP sourceP (P.filter (> maxValue))+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.filtered (> maxValue))+          , bench "streaming" $ nfIO $ runIOS sourceS (S.filter (> maxValue))+          , bench "streamly" $ nfIO $ runIOA sourceA (A.filter (> maxValue))+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.filter (> maxValue))+          ]+        , bgroup "filter-all-in"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.filter (<= maxValue))+          , bench "pipes" $ nfIO $ runIOP sourceP (P.filter (<= maxValue))+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.filtered (<= maxValue))+          , bench "streaming" $ nfIO $ runIOS sourceS (S.filter (<= maxValue))+          , bench "streamly" $ nfIO $ runIOA sourceA (A.filter (<= maxValue))+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.filter (<= maxValue))+          ]+        , bgroup "take-one"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.isolate 1)+          , bench "pipes" $ nfIO $ runIOP sourceP (P.take 1)+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.taking 1)+          , bench "streaming" $ nfIO $ runIOS sourceS (S.take 1)+          , bench "streamly" $ nfIO $ runIOA sourceA (A.take 1)+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.take 1)+          ]+          -- XXX variance need to be fixed, value used is not correct+        , bgroup "take-all"+          [ bench "conduit" $ nfIO $ runIOC sourceC (C.isolate maxValue)+          , bench "pipes" $ nfIO $ runIOP sourceP (P.take maxValue)+          , bench "machines" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.taking maxValue)+          , bench "streaming" $ nfIO $ runIOS sourceS (S.take maxValue)+          , bench "streamly" $ nfIO $ runIOA sourceA (A.take maxValue)+          -- , bench "list-transformer" $ nfIO $ (runIdentity . L.runListT) (L.take value sourceL :: L.ListT Identity Int)+          , bench "vector" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.take maxValue)+          ]+        , bgroup "takeWhile-true"+          [ bench "conduit"   $ nfIO $ runIOC sourceC (CC.takeWhile (<= maxValue))+          , bench "pipes"     $ nfIO $ runIOP sourceP (P.takeWhile (<= maxValue))+          , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.takingWhile (<= maxValue))+          , bench "streaming" $ nfIO $ runIOS sourceS (S.takeWhile (<= maxValue))+          , bench "streamly"   $ nfIO $ runIOA sourceA (A.takeWhile (<= maxValue))+          , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.takeWhile (<= maxValue))+          ]+        , bgroup "drop-all"+          [ bench "conduit"   $ nfIO $ runIOC sourceC (C.drop maxValue)+          , bench "pipes"     $ nfIO $ runIOP sourceP (P.drop maxValue)+          , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.dropping maxValue)+          , bench "streaming" $ nfIO $ runIOS sourceS (S.drop maxValue)+          , bench "streamly"   $ nfIO $ runIOA sourceA (A.drop maxValue)+          -- , bench "simple-conduit" $ whnf drainSC (SC.dropC value)+          --, bench "list-transformer" $ whnf (runIdentity . L.runListT) (L.drop value sourceL :: L.ListT Identity Int)+          , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.drop maxValue)+          ]+        , bgroup "dropWhile-true"+          [ bench "conduit"   $ nfIO $ runIOC sourceC (CC.dropWhile (<= maxValue))+          , bench "pipes"     $ nfIO $ runIOP sourceP (P.dropWhile (<= maxValue))+          , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) (M.droppingWhile (<= maxValue))+          , bench "streaming" $ nfIO $ runIOS sourceS (S.dropWhile (<= maxValue))+          , bench "streamly"   $ nfIO $ runIOA sourceA (A.dropWhile (<= maxValue))+          , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (V.dropWhile (<= maxValue))+          ]+        ]+    , bgroup "zip"+        [ bench "conduit" $ nfIO $ C.runConduit $ (C.getZipSource $ (,) <$> C.ZipSource sourceC <*> C.ZipSource sourceC) C..| C.sinkNull+        , bench "pipes" $ nfIO $ P.runEffect $ P.for (P.zip sourceP sourceP) P.discard+        , bench "machines" $ nfIO $ getRandom >>= \v1 -> getRandom >>= \v2 -> M.runT_ (M.capT (sourceM v1) (sourceM v2) M.zipping)+        , bench "streaming" $ nfIO $ S.effects (S.zip sourceS sourceS)+        , bench "streamly" $ nfIO $ A.runStreamT $ (A.zipWith (,) sourceA sourceA)+        , bench "vector" $ nfIO $ getRandom >>= \v1 -> getRandom >>= \v2 -> V.mapM_ return $ (V.zipWith (,) (sourceV v1) (sourceV v2))+        ]+    -- Composing multiple stages of a pipeline+    , bgroup "compose"+        [+        {-+          let f x =+                  if (x `mod` 4 == 0)+                  then+                      randomIO+                  else return x+        -}+          let f = return+              c = C.mapM f+              p = P.mapM f+              m = M.autoM f+              s = S.mapM f+              a = A.mapM f+              u = V.mapM f+              lb = lift . f+              l = lift . f+              lg = lift . f+          in bgroup "mapM"+            [ bench "conduit"   $ nfIO $ runIOC sourceC $ c C..| c C..| c C..| c+            , bench "pipes"     $ nfIO $ runIOP sourceP $ p P.>-> p P.>-> p P.>-> p+            , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ m M.~> m M.~> m M.~> m+            , bench "streaming" $ nfIO $ runIOS sourceS $ \x -> s x & s & s & s+            , bench "streamly"   $ nfIO $ runIOA sourceA $ \x -> a x & a & a & a+            , bench "list-t"    $ nfIO $ runIOLT sourceLT $ \x -> lb x >>= lb >>= lb >>= lb+            , bench "list-transformer" $ nfIO $ runIOL sourceL $ \x -> l x >>= l >>= l >>= l+            , bench "logict"    $ nfIO $ getRandom >>= \v -> runIOLG (sourceLG v) $ \x -> lg x >>= lg >>= lg >>= lg+            , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> u x & u & u & u+            ]++        -- XXX should we use a monadic mapM instead?+        , let m = M.mapping (subtract 1) M.~> M.filtered (<= maxValue)+              s = S.filter (<= maxValue) . S.map (subtract 1)+              a = A.filter (<= maxValue) . fmap (subtract 1)+              p = P.map (subtract 1)  P.>-> P.filter (<= maxValue)+              c = C.map (subtract 1)  C..| C.filter (<= maxValue)+              u = V.filter (<= maxValue) . V.map (subtract 1)+          in bgroup "map-with-all-in-filter"+            [ bench "conduit"   $ nfIO $ runIOC sourceC $ c C..| c C..| c C..| c+            , bench "pipes"     $ nfIO $ runIOP sourceP $ p P.>-> p P.>-> p P.>-> p+            , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ m M.~> m M.~> m M.~> m+            , bench "streaming" $ nfIO $ runIOS sourceS $ \x -> s x & s & s & s+            , bench "streamly" $ nfIO $ runIOA sourceA $ \x -> a x & a & a & a+            , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> u x & u & u & u+            ]++        -- Compose multiple ops, all stages letting everything through.+        -- Note, IO monad makes a big difference especially for machines.+        , let m = M.filtered (<= maxValue)+              a = A.filter (<= maxValue)+              s = S.filter (<= maxValue)+              p = P.filter (<= maxValue)+              c = C.filter (<= maxValue)+              u = V.filter (<= maxValue)+          in bgroup "all-in-filters"+            [ bench "conduit"   $ nfIO $ runIOC sourceC $ c C..| c C..| c C..| c+            , bench "pipes"     $ nfIO $ runIOP sourceP $ p P.>-> p P.>-> p P.>-> p+            , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ m M.~> m M.~> m M.~> m+            , bench "streaming" $ nfIO $ runIOS sourceS $ \x -> s x & s & s & s+            , bench "streamly" $ nfIO $ runIOA sourceA $ \x -> a x & a & a & a+            , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> u x & u & u & u+            ]++          -- how filtering affects the subsequent composition+        , let m = M.filtered (> maxValue)+              a = A.filter   (> maxValue)+              s = S.filter   (> maxValue)+              p = P.filter   (> maxValue)+              c = C.filter   (> maxValue)+              u = V.filter   (> maxValue)+          in bgroup "all-out-filters"+            [ bench "conduit"   $ nfIO $ runIOC sourceC $ c C..| c C..| c C..| c+            , bench "pipes"     $ nfIO $ runIOP sourceP $ p P.>-> p P.>-> p P.>-> p+            , bench "machines"  $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ m M.~> m M.~> m M.~> m+            , bench "streaming" $ nfIO $ runIOS sourceS $ \x -> s x & s & s & s+            , bench "streamly" $ nfIO $ runIOA sourceA $ \x -> a x & a & a & a+            , bench "vector"    $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> u x & u & u & u+            ]+        ]+    , bgroup "compose-scaling"+        [+        -- Scaling with same operation in sequence+          let f = M.filtered (<= maxValue)+          in bgroup "machines-filters"+            [ bench "1" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) f+            , bench "2" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ f M.~> f+            , bench "3" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ f M.~> f M.~> f+            , bench "4" $ nfIO $ getRandom >>= \v -> runIOM (sourceM v) $ f M.~> f M.~> f M.~> f+            ]+        , let f = A.filter (<= maxValue)+          in bgroup "streamly-filters"+            [ bench "1" $ nfIO $ runIOA sourceA (\x -> f x)+            , bench "2" $ nfIO $ runIOA sourceA $ \x -> f x & f+            , bench "3" $ nfIO $ runIOA sourceA $ \x -> f x & f & f+            , bench "4" $ nfIO $ runIOA sourceA $ \x -> f x & f & f & f+            ]+        , let f = S.filter (<= maxValue)+          in bgroup "streaming-filters"+            [ bench "1" $ nfIO $ runIOS sourceS (\x -> f x)+            , bench "2" $ nfIO $ runIOS sourceS $ \x -> f x & f+            , bench "3" $ nfIO $ runIOS sourceS $ \x -> f x & f & f+            , bench "4" $ nfIO $ runIOS sourceS $ \x -> f x & f & f & f+            ]+        , let f = P.filter (<= maxValue)+          in bgroup "pipes-filters"+            [ bench "1" $ nfIO $ runIOP sourceP f+            , bench "2" $ nfIO $ runIOP sourceP $ f P.>-> f+            , bench "3" $ nfIO $ runIOP sourceP $ f P.>-> f P.>-> f+            , bench "4" $ nfIO $ runIOP sourceP $ f P.>-> f P.>-> f P.>-> f+            ]+        , let f = C.filter (<= maxValue)+          in bgroup "conduit-filters"+            [ bench "1" $ nfIO $ runIOC sourceC f+            , bench "2" $ nfIO $ runIOC sourceC $ f C..| f+            , bench "3" $ nfIO $ runIOC sourceC $ f C..| f C..| f+            , bench "4" $ nfIO $ runIOC sourceC $ f C..| f C..| f C..| f+            ]+         , let f = V.filter (<= maxValue)+          in bgroup "vector-filters"+            [ bench "1" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) (\x -> f x)+            , bench "2" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> f x & f+            , bench "3" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> f x & f & f+            , bench "4" $ nfIO $ getRandom >>= \v -> runIOV (sourceV v) $ \x -> f x & f & f & f+            ]+        ]+  ]
+ Charts.hs view
@@ -0,0 +1,200 @@+import Control.Arrow (second)+import Data.Char (isSpace)+import Data.List.Split (splitOn)+import Data.Maybe (fromMaybe, catMaybes, fromJust)+import Debug.Trace (trace)+import System.Directory (createDirectoryIfMissing)+import System.Environment (getArgs)+import System.Process.Typed (readProcess_)+import Text.CSV (CSV, parseCSVFromFile)++import qualified Data.Text.Lazy.Encoding as T+import qualified Data.Text.Lazy as T++import Graphics.Rendering.Chart.Easy+import Graphics.Rendering.Chart.Backend.Diagrams++-------------------------------------------------------------------------------+-- Configurable stuff+-------------------------------------------------------------------------------++outputDir :: String+outputDir = "charts"++packages :: [String]+packages = ["streamly", "streaming", "pipes", "conduit", "machines", "vector"]++-- pairs of benchmark group titles and corresponding list of benchmark+-- prefixes i.e. without the package name at the end.+bmGroups :: [(String, [String])]+bmGroups =+    [+      -- Operations are listed in increasing cost order+      ( "All Operations at a Glance (Shorter is Faster)"+      , [+        -- "filtering/take-one"+          "elimination/toNull"+        , "filtering/drop-all"+        , "elimination/last"+        , "elimination/fold"++        , "filtering/filter-all-out"+        , "filtering/dropWhile-true"+        , "filtering/take-all"+        , "filtering/takeWhile-true"+        , "transformation/map"+        , "filtering/filter-all-in"+        , "filtering/filter-even"++        , "elimination/scan"+        , "transformation/mapM"+        ,  "zip"++        , "elimination/toList"+        , "elimination/concat"+        ]+      )+    , ( "Discarding and Folding (Shorter is Faster)"+      , [+        -- "filtering/take-one"+          "elimination/toNull"+        , "filtering/drop-all"+        , "elimination/last"+        , "elimination/fold"+        ]+      )+    , ( "Pure Transformation and Filtering (Shorter is Faster)"+      , [+          "filtering/filter-all-out"+        , "filtering/dropWhile-true"+        , "filtering/take-all"+        , "filtering/takeWhile-true"+        , "transformation/map"+        , "filtering/filter-all-in"+        , "filtering/filter-even"+        , "elimination/scan"+        ]+      )+    , ( "Monadic Transformation (Shorter is Faster)"+      , [+          "transformation/mapM"+        ]+      )+    , ( "Folding to List (Shorter is Faster)"+      , [+          "elimination/toList"+        ]+      )+    , ( "Zipping and Concating Streams (Shorter is Faster)"+      , [ "zip"+        , "elimination/concat"+        ]+      )+    , ( "Composing Pipeline Stages (Shorter is Faster)"+      , [+          "compose/all-out-filters"+        , "compose/all-in-filters"+        , "compose/map-with-all-in-filter"+        , "compose/mapM"+        ]+      )+    ]++-------------------------------------------------------------------------------++-- "values" has results for each package for each title in bmTitles+genGroupGraph :: String -> [String] -> [(String, [Maybe Double])] -> IO ()+genGroupGraph bmGroupName bmTitles values =+    toFile def (outputDir+                ++ "/"+                -- links in README.rst eat up the space so we match the same+                ++ (filter (not . isSpace) (takeWhile (/= '(') bmGroupName))+                ++ ".svg") $ do+        layout_title .= bmGroupName+        layout_title_style . font_size .= 25+        layout_x_axis . laxis_generate .= autoIndexAxis (map fst values)+        layout_x_axis . laxis_style . axis_label_style . font_size .= 12++        -- layout_y_axis . laxis_override .= axisGridAtTicks+        let modifyLabels ad = ad {+                _axis_labels = map (map (second (++ " ms"))) (_axis_labels ad)+            }+        layout_y_axis . laxis_override .= modifyLabels+        -- XXX We are mapping a missing value to 0, can we label it missing+        -- instead?+        let modifyVal x = map ((*1000) . fromMaybe 0) (snd x)+        plot $ fmap plotBars $ bars bmTitles (addIndexes (map modifyVal values))++-- Given a package name (e.g. streaming) and benchmark prefixes (e.g.+-- [elimination/null, elimination/toList]) get the corresponding results e.g.+-- [8.1 ms, 5.4 ms]. The corresponding result file entries will have+-- elimination/null/streaming etc. as the names of the entries.+getResultsForPackage :: CSV -> String -> [String] -> [Maybe Double]+getResultsForPackage csvData pkgname bmPrefixes =+      map (getBenchmarkMean csvData)+    $ map (++ "/" ++ pkgname) bmPrefixes++    where++    getBenchmarkMean entries bmname =+        case filter ((== bmname) .  head) entries of+            [] -> trace+                ("Warning! Benchmark [" ++ bmname ++"] not found in csv data")+                Nothing+            xs -> Just (read ((last xs) !! 1))++genOneGraph :: CSV -> [(String, String)] -> (String, [String]) -> IO ()+genOneGraph csvData pkginfo (bmGroupTitle, prefixes) =+    genGroupGraph bmGroupTitle bmTitles bmResults++    where++    bmTitles = map (last . splitOn "/" ) prefixes++    pkgName = fst+    pkgVersion = snd+    pkgNameWithVersion pkgInfo = pkgName pkgInfo ++ "-" ++ pkgVersion pkgInfo+    pkgGetResults pkgInfo =+        let vals = getResultsForPackage csvData (pkgName pkgInfo) prefixes+        in (pkgNameWithVersion pkgInfo, vals)++    -- this produces results for all packages for all prefixes+    -- [(packagenamewithversion, [Maybe Double])]+    bmResults = map pkgGetResults pkginfo++genGraphs :: CSV -> [(String, String)] -> IO ()+genGraphs csvData pkginfo = mapM_ (genOneGraph csvData pkginfo) bmGroups++-- XXX display GHC version as well+-- XXX display the OS/arch+-- XXX fix the y axis labels+-- XXX fix the legend position+main :: IO ()+main = do+    args <- getArgs++    createDirectoryIfMissing True outputDir++    (out, _) <- readProcess_ "stack --system-ghc list-dependencies --bench"++    -- Get our streaming packages and their versions+    let match [] = Nothing+        match (_ : []) = Nothing+        match (x : y : _) =+            case elem x packages of+                False -> Nothing+                True -> Just (x, y)+        pkginfo =+              catMaybes+            $ map match+            $ map words (lines (T.unpack $ T.decodeUtf8 out))++    -- order them in the order specified in packages so that the order is+    -- can be controlled by the user.+    let pkginfo' = map (\x -> (x, fromJust $ lookup x pkginfo)) packages++    csvData <- parseCSVFromFile (head args)+    case csvData of+        Left e -> error $ show e+        Right dat -> genGraphs dat pkginfo'+    return ()
+ LICENSE view
@@ -0,0 +1,21 @@+Copyright © 2017 Harendra Kumar++Permission is hereby granted, free of charge, to any person obtaining a copy of+this software and associated documentation files (the "Software"), to deal in+the Software without restriction, including without limitation the rights to+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of+the Software, and to permit persons to whom the Software is furnished to do so,+subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.++Please also see the licenses for other contributions in the "licenses"+directory.
+ README.rst view
@@ -0,0 +1,214 @@+Streaming Benchmarks+--------------------++Comprehensive, carefully crafted benchmarks for streaming operations and their+comparisons across notable Haskell streaming libraries including `streaming`,+`machines`, `pipes`, `conduit` and `streamly`. `Streamly+<https://github.com/composewell/streamly>`_ is a brand new streaming library+with beautiful high level and composable concurrency built-in, it is the+primary motivation for these benchmarks. We go to great lengths to make sure+that the benchmarks are correct, fair and reproducible. Please report if you+find something that is not right.++Benchmarks & Results+--------------------++In all the benchmarks we work on a stream of a million consecutive numbers. We+start the sequence using a random number between 1 and 1000 and enumerate it to+make a total of a million elements using the streaming library's native+sequence enumeration API. Note that the efficiency of this sequence generation+may affect all performance numbers of the library because this is a constant+cost involved in all the benchmarks.++Note that, these benchmarks show results for conduit-1.3.0 which is a recently+released major version, it perhaps requires some work to get at par with the+earlier version i.e.+conduit-1.2.13.1 `which showed significantly better performance+<https://github.com/composewell/streaming-benchmarks/blob/269ac94fc59c76267b89b07690d9ea290096b95b/charts/AllOperationsataGlance.svg>`_+compared to the newer version.++When choosing a streaming library to use we should not be over obsessed about+the performance numbers as long as the performance is within reasonable bounds.+Whether the absolute performance or the differential among various libraries matters+or not may depend on your workload. If the cost of processing the data is+significantly higher then the streaming operations' overhead will just pale in+comparison and may not matter at all. Unless you are performing huge number of+tiny operations, performance difference may not be significant.++Composing Pipeline Stages+~~~~~~~~~~~~~~~~~~~~~~~~~++These benchmarks compare the performance when multiple operations are composed+serially in a pipeline. This is how the streaming libraries are supposed to be+used in real applications.++The `mapM` benchmark introduces four stages of `mapM` between the source and+the sink.++`all-in-filters` composes four stages of a `filter` operation that passes all+the items through.  Note that passing or blocking nature of the filter may+impact the results. Some libraries can do blocking more optimally by short+circuiting.++`all-out-filters` composes four stages of a `filter` operation that `blocks`+all the items i.e. does not let anything pass through.++The `map-with-all-in-filter` benchmark introduces four identical stages between+the source and the sink where each stage performs a simple `map` operation+followed by a `filter` operation that passes all the items through.++.. image:: charts/Composing Pipeline Stages.svg+  :alt: Composing Pipeline Stages++Individual Operations+~~~~~~~~~~~~~~~~~~~~~++This chart shows microbenchmarks for all individual streaming operations for a+quick comparison. Operations are ordered more or less by increasing cost for+better visualization. If an operation is not present in a library then an empty+space is displayed instead of a colored bar in its slot. See the following+sections for details about what the benchmarks do.++.. image:: charts/All Operations at a Glance.svg+  :alt: All Operations at a Glance++Discarding and Folding+^^^^^^^^^^^^^^^^^^^^^^++This chart shows the cheapest of all operations, they include operations that+iterate over the stream and either discard all the elements or fold them to a+single value. They all do similar stuff and are generally expected to have+similar cost.  Benchmarks include:++* `toNull:` Just discards all the elements in the stream.+* `drop-all`: drops ``n`` elements from the stream where ``n`` is set to the+  length of the stream.+* `last`: drops all the elements except the last one.+* `fold`: adds all the elements in the stream to produces the sum.++.. image:: charts/Discarding and Folding.svg+  :alt: Discarding and Folding++Pure Transformation and Filtering+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^++This is the next category which is a bit costlier than the previous one. Unlike+previous category these operations inspect the elements in the stream and+form a transformed stream based on a function on the value. Benchmarks include:++* `filter-all-out`: A filter that discards all the elements in the stream.+* `filter-all-in`: A filter that retains all the elements in the stream.+* `take-all`: take `n` elements from the stream where `n` is set to the length+  of the stream. Effectively iterates through the stream and retains all of it.+* `takeWhile-true`: retains all elements of the stream using a condition that+  always wvaluates to true.+* `map`: A pure transformation that increments each element by 1.+* `filter-even`: A filter that passes even elements in the stream i.e. half the+  elements are kept and the other half discarded.+* `scan`: scans the stream using ``+`` operation.++.. image:: charts/Pure Transformation and Filtering.svg+  :alt: Pure Transformation and Filtering++Monadic Transformation+^^^^^^^^^^^^^^^^^^^^^^++This benchmark compares the monadic transformation of the stream using+``mapM``.++.. image:: charts/Monadic Transformation.svg+  :alt: Monadic Transformation++Folding to List+^^^^^^^^^^^^^^^++This benchmark compares folding the stream to a list.++.. image:: charts/Folding to List.svg+  :alt: Folding to List++Zip and Concat+^^^^^^^^^^^^^^++Zip combines corresponding elements of the two streams together. Concat turns a+stream of containers into a stream of their elements.++.. image:: charts/Zipping and Concating Streams.svg+  :alt: Zipping and Concating Streams++Studying the Scaling of Composition+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++This category of benchmarks studies the effect of adding more stages in a+composition pipeline. For each library it displays the results when 1, 2, 3 or+4 pipeline stages are used. There are no graphs you can see the results in the+benchmark output.++How to Run+----------++::++  ./run.sh++After running you can find the charts generated in the ``charts`` directory. If+you are impatient use ``./run.sh --quick`` and you will get the results much+sooner though a tiny bit less precise. Note that quick mode won't generate the+graphs unless the latest ``gauge`` is used from github repo.++Note that if different optimization flags are used on different packages,+performance can sometimes badly suffer because of GHC inlining and+specialization not working optimally.  If you  want to be aboslutely sure that+all packages and dependencies are compiled with the same optimization flags+(``-O2``) use ``run.sh --pedantic``, it will install the stack snapshot in a+private directory under the current directory and build them fresh with the ghc+flags specified in ``stack-pedantic.yaml``. Be aware that this will require 1-2+GB extra disk space.++Important Points about Benchmarking Methodology+-----------------------------------------------++``IO Monad:`` We run the benchmarks in the IO monad so that they are close to+real life usage. Note that most existing streaming benchmarks use pure code or+Identity monad which may produce entirely different results.++``Benchmarking Tool:`` We use the `gauge+<https://github.com/vincenthz/hs-gauge>`_ package instead of criterion.  We+spent a lot of time figuring out why benchmarking was not producing accurate+results. Criterion had several bugs due to which results were not reliable. We+fixed those bugs in ``gauge``. For example due to GC or CAF evaluation+interaction across benchmarks, the results of benchmarks running later in the+sequence were sometimes totally off the mark. We fixed that by running each+benchmark in a separate process in gauge. Another bug caused criterion to+report wrong mean.++``Iterations:`` We pass a million elements through the streaming pipelines. We+do not rely on the benchmarking tool for this, it is explicitly done by the+benchmarking code and the benchmarking tool is asked to perform just one+iteration. We added fine grained control in `gauge+<https://github.com/vincenthz/hs-gauge>`_ to be able to do this.++``Effects of Optimizations:`` In some cases fusion or other optimizations can+just optimize out everything and produce ridiculously low results. To avoid+that we generate random numbers in the IO monad and pass those through the+pipeline rather than using some constant or predictable source.++``GHC Optimization Flags:`` To make sure we are comparing fairly we make sure+that we compile the benchmarking code, the library code as well as all+dependencies using exactly the same GHC flags. GHC inlining and specialization+optimizations can make the code unpredictable if mixed flags are used. See the+``--pedantic`` option of the ``run.sh`` script.++``Benchmark Categories:`` We have two categories of benchmarks, one to measure+the performance of individual operations in isolation and the other to measure+the performance when multiple similar or different operations are composed+together in a pipeline.++Benchmarking Errors+-------------------++Benchmarking is a tricky business. Though the benchmarks have been carefully+designed there may still be issues with the way benchmarking is being done or+the way they have been coded. If you find that something is being measured+unfairly or incorrectly please bring it to our notice by raising an issue or+sending an email.
+ licenses/LICENSE.machines view
@@ -0,0 +1,30 @@+Copyright 2012-2015 Edward Kmett, Runar Bjarnason, Paul Chiusano++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++1. Redistributions of source code must retain the above copyright+   notice, this list of conditions and the following disclaimer.++2. Redistributions in binary form must reproduce the above copyright+   notice, this list of conditions and the following disclaimer in the+   documentation and/or other materials provided with the distribution.++3. Neither the name of the author nor the names of his contributors+   may be used to endorse or promote products derived from this software+   without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ licenses/Readme.txt view
@@ -0,0 +1,3 @@+The benchmarking code in this package was originally adapted from the+benchmarks in the machines package. The original license for that code is+included in this directory as LICENSE.machines.
+ run.sh view
@@ -0,0 +1,109 @@+#!/bin/bash++print_help () {+  echo "Usage: $0 [--quick] [--pedantic] [--no-graph] [--no-measure] <benchmark-name or prefix> [min-samples]"+  echo "Any arguments after a '--' will be passed as it is to guage"+  exit+}++# $1: message+die () {+  >&2 echo -e "Error: $1"+  exit 1+}++while test -n "$1"+do+  case $1 in+    -h|--help|help) print_help ;;+    --quick) QUICK=1; shift ;;+    --pedantic) PEDANTIC=1; shift ;;+    --no-graph) GRAPH=0; shift ;;+    --no-measure) MEASURE=0; shift ;;+    --) shift; break ;;+    -*|--*) print_help ;;+    *) break ;;+  esac+done++STACK=stack+if test "$PEDANTIC" = "1"+then+  GHC_PATH=`$STACK path --compiler-bin`+  export PATH=$GHC_PATH:$PATH+  mkdir -p .stack-root+  export STACK_ROOT=`pwd`/.stack-root+  STACK="$STACK --system-ghc --stack-yaml stack-pedantic.yaml"+fi++echo "Using stack command [$STACK]"+$STACK build --bench --no-run-benchmarks || die "build failed"++# We run the benchmarks in isolation in a separate process so that different+# benchmarks do not interfere with other. To enable that we need to pass the+# benchmark exe path to guage as an argument. Unfortunately it cannot find its+# own path currently.++# The path is dependent on the architecture and cabal version.+# Use this command to find the exe if this script fails with an error:+# find .stack-work/ -type f -name "benchmarks"++enable_isolated () {+  local PROG=`$STACK path --dist-dir`/build/benchmarks/benchmarks+  if test -x "$PROG"+  then+    BENCH_PROG="--measure-with $PROG"+  else+    echo+    echo "WARNING! benchmark binary [$PROG] not found or not executable"+    echo "WARNING! not using isolated measurement."+    echo+  fi+}++enable_isolated++# --min-duration 0 means exactly one iteration per sample. We use a million+# iterations in the benchmarking code explicitly and do not use the iterations+# done by the benchmarking tool.+#+# Benchmarking tool by default discards the first iteration to remove+# aberrations due to initial evaluations etc. We do not discard it because we+# are anyway doing iterations in the benchmarking code and many of them so that+# any constant factor gets amortized and anyway it is a cost that we pay in+# real life.+#+# We can pass --min-samples value from the command line as second argument+# after the benchmark name in case we want to use more than one sample.++if test "$QUICK" = "1"+then+  ENABLE_QUICK="--quick"+fi++if test "$MEASURE" != "0"+  then+  if test -e results.csv+  then+    mv -f -v results.csv results.csv.prev+  fi++  # We set min-samples to 1 so that we run with default benchmark duration of 5+  # seconds, whatever number of samples are possible in that.+  # We run just one iteration for each sample. Anyway the default is to run+  # for 30 ms and most our benchmarks are close to that or more.+  $STACK bench --benchmark-arguments "$ENABLE_QUICK \+    --include-first-iter \+    --min-samples 1 \+    --min-duration 0 \+    --csv=results.csv \+    -v 2 \+    $BENCH_PROG $*" || die "Benchmarking failed"+fi++if test "$GRAPH" != "0"+then+  echo+  echo "Generating charts from results.csv..."+  $STACK exec makecharts results.csv+fi
+ stack-pedantic.yaml view
@@ -0,0 +1,22 @@+resolver: lts-11.0+packages:+- '.'+extra-deps:+  - gauge-0.2.1+  - list-transformer-1.0.3+  - streamly-0.1.1++  # for lts-11.0+  - Chart-diagrams-1.8.3+  - SVGFonts-1.6.0.3+  - diagrams-core-1.4.0.1+  - diagrams-lib-1.4.2+  - diagrams-postscript-1.4+  - diagrams-svg-1.4.1.1+  - diagrams-solve-0.1.1+  - dual-tree-0.2.1+  - lens-4.15.4+  - free-4.12.4++ghc-options:+    "$everything": -O2
+ stack.yaml view
@@ -0,0 +1,19 @@+resolver: lts-11.0+packages:+- '.'+extra-deps:+  - gauge-0.2.1+  - list-transformer-1.0.3+  - streamly-0.1.1++  # for lts-11.0+  - Chart-diagrams-1.8.3+  - SVGFonts-1.6.0.3+  - diagrams-core-1.4.0.1+  - diagrams-lib-1.4.2+  - diagrams-postscript-1.4+  - diagrams-svg-1.4.1.1+  - diagrams-solve-0.1.1+  - dual-tree-0.2.1+  - lens-4.15.4+  - free-4.12.4
+ streaming-benchmarks.cabal view
@@ -0,0 +1,98 @@+name:          streaming-benchmarks+category:      Benchmark+version:       0.1.0+license:       MIT+license-file:  LICENSE+author:        Harendra Kumar+maintainer:    Harendra Kumar+stability:     provisional+homepage:      http://github.com/composewell/streaming-benchmarks+bug-reports:   http://github.com/composewell/streaming-benchmarks/issues+copyright:     Copyright (c) 2017 Harendra Kumar+synopsis:      Benchmarks to compare streaming packages+description:+  Comprehensive, carefully crafted benchmarks for streaming operations and+  their comparisons across notable Haskell streaming libraries including+  `streaming`, `machines`, `pipes`, `conduit` and `streamly`.+  <http://hackage.haskell.org/package/streamly Streamly> is a new+  streaming library with high level and composable concurrency built-in, it is+  the primary motivation for these benchmarks. We have put a lot of effort to+  make sure that the benchmarks are correct, fair and reproducible.  Please+  report if you find something that is not right.+  .+  If you are using @stack@ then use @./run.sh@ to run the benchmarks;+  charts will be generated in the `charts` directory.+  .+  With any build tool, run the benchmarks with+  @--csv=results.csv@ as arguments and then use @makecharts results.csv@ to+  create the charts. In case you want to be pedantic about accurate results+  then you can run the benchmarks in the same way as @run.sh@ invokes them.++cabal-version: >= 1.10+tested-with: GHC==8.2.2+build-type:    Simple+extra-source-files:+  run.sh+  README.rst+  licenses/Readme.txt+  licenses/LICENSE.machines+  stack.yaml+  stack-pedantic.yaml++source-repository head+  type: git+  location: git://github.com/composewell/streaming-benchmarks.git++benchmark benchmarks+  default-language: Haskell2010+  type:             exitcode-stdio-1.0+  hs-source-dirs:   .+  main-is:          Benchmarks.hs+  ghc-options: -O2 -Wall -with-rtsopts "-T"+  if impl(ghc >= 8.0)+    ghc-options:    -Wcompat+                    -Wunrecognised-warning-flags+                    -Widentities+                    -Wincomplete-record-updates+                    -Wincomplete-uni-patterns+                    -Wredundant-constraints+                    -Wnoncanonical-monad-instances+                    -Wnoncanonical-monadfail-instances++  build-depends:+    base                == 4.*,+    deepseq             >= 1.4.0 && < 1.5,+    gauge               >= 0.2.1 && < 0.3,+    mtl                 >= 2     && < 2.3,+    random              >= 1.0   && < 2.0,+    transformers        >= 0.4   && < 0.6,++    conduit             >= 1.3   && < 1.4,+    list-transformer    >= 1.0.2 && < 1.1,+    list-t              >= 0.4.6 && < 1.1,+    logict              >= 0.5.0 && < 0.7,+    machines            >= 0.6.0 && < 0.7,+    pipes               >= 4     && < 4.4,+    -- does not build with lts-11.0+    -- simple-conduit      >= 0.4.0 && < 0.7,+    streaming           >= 0.1.4 && < 0.3,+    vector              >= 0.12  && < 0.13,+    streamly            >= 0.1.1 && < 0.2++executable makecharts+  default-language: Haskell2010+  default-extensions: OverloadedStrings+  hs-source-dirs:   .+  main-is: Charts.hs+  ghc-options: -Wall++  build-depends:+      base              == 4.*+    , bytestring        >= 0.9     && < 0.11+    , Chart             >= 1.6     && < 1.9+    , Chart-diagrams    >= 1.6     && < 1.9+    , csv               >= 0.1     && < 0.2+    , directory         >= 1.2     && < 1.4+    , split             >= 0.2     && < 0.3+    , text              >= 1.1.1   && < 1.3+    , typed-process     >= 0.1.0.0 && < 0.3