diff --git a/Benchmarks.hs b/Benchmarks.hs
new file mode 100644
--- /dev/null
+++ b/Benchmarks.hs
@@ -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
+            ]
+        ]
+  ]
diff --git a/Charts.hs b/Charts.hs
new file mode 100644
--- /dev/null
+++ b/Charts.hs
@@ -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 ()
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.rst b/README.rst
new file mode 100644
--- /dev/null
+++ b/README.rst
@@ -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.
diff --git a/licenses/LICENSE.machines b/licenses/LICENSE.machines
new file mode 100644
--- /dev/null
+++ b/licenses/LICENSE.machines
@@ -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.
diff --git a/licenses/Readme.txt b/licenses/Readme.txt
new file mode 100644
--- /dev/null
+++ b/licenses/Readme.txt
@@ -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.
diff --git a/run.sh b/run.sh
new file mode 100644
--- /dev/null
+++ b/run.sh
@@ -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
diff --git a/stack-pedantic.yaml b/stack-pedantic.yaml
new file mode 100644
--- /dev/null
+++ b/stack-pedantic.yaml
@@ -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
diff --git a/stack.yaml b/stack.yaml
new file mode 100644
--- /dev/null
+++ b/stack.yaml
@@ -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
diff --git a/streaming-benchmarks.cabal b/streaming-benchmarks.cabal
new file mode 100644
--- /dev/null
+++ b/streaming-benchmarks.cabal
@@ -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
