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 +449/−0
- Charts.hs +200/−0
- LICENSE +21/−0
- README.rst +214/−0
- licenses/LICENSE.machines +30/−0
- licenses/Readme.txt +3/−0
- run.sh +109/−0
- stack-pedantic.yaml +22/−0
- stack.yaml +19/−0
- streaming-benchmarks.cabal +98/−0
+ 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