packages feed

machines-0.6.4: benchmarks/Benchmarks.hs

module Main (main) where

import Control.Applicative
import Data.Function ((&))
import Control.Monad (void)
import Control.Monad.Identity
import Criterion.Main
import Data.Void
import qualified Data.Conduit      as C
import qualified Data.Conduit.Combinators as CC
import qualified Data.Conduit.List as C
import qualified Data.Machine      as M
import qualified Pipes             as P
import qualified Pipes.Prelude     as P
import qualified Streaming.Prelude as S
import Prelude

value :: Int
value = 1000000

drainM :: M.ProcessT Identity Int o -> ()
drainM m = runIdentity $ M.runT_ (sourceM M.~> m)

drainMIO :: M.ProcessT IO Int o -> IO ()
drainMIO m = M.runT_ (sourceM M.~> m)

drainP :: P.Proxy () Int () a Identity () -> ()
drainP p = runIdentity $ P.runEffect $ P.for (sourceP P.>-> p) P.discard

drainPIO :: P.Proxy () Int () a IO () -> IO ()
drainPIO p = P.runEffect $ sourceP P.>-> p P.>-> P.mapM_ (\_ -> return ())

drainC :: C.ConduitT Int a Identity () -> ()
drainC c = runIdentity $ C.runConduit $ (sourceC C..| c) C..| C.sinkNull

drainCIO :: C.ConduitT Int a IO () -> IO ()
drainCIO c = C.runConduit $ (sourceC C..| c) C..| C.mapM_ (\_ -> return ())

drainSC :: C.ConduitT Int Void Identity b -> ()
drainSC c = runIdentity $ void $! C.runConduit $ sourceC C..| c

drainS :: (S.Stream (S.Of Int) Identity () -> S.Stream (S.Of Int) Identity ())
    -> ()
drainS s = runIdentity $ S.effects $ sourceS & s

drainSIO :: (S.Stream (S.Of Int) IO () -> S.Stream (S.Of Int) IO ()) -> IO ()
drainSIO s = sourceS & s & S.mapM_ (\_ -> return ())

sourceM :: M.Source Int
sourceM = M.enumerateFromTo 1 value

sourceC :: Monad m => C.ConduitT i Int m ()
sourceC = C.enumFromTo 1 value

sourceP :: Monad m => P.Producer' Int m ()
sourceP = P.each [1..value]

sourceS :: Monad m => S.Stream (S.Of Int) m ()
sourceS = S.each [1..value]

main :: IO ()
main =
  defaultMain
  [ bgroup "map"
      [ bench "machines" $ whnf drainM (M.mapping (+1))
      , bench "streaming" $ whnf drainS (S.map (+1))
      , bench "pipes" $ whnf drainP (P.map (+1))
      , bench "conduit" $ whnf drainC (C.map (+1))
      ]
  , bgroup "drop"
      [ bench "machines" $ whnf drainM (M.dropping value)
      , bench "streaming" $ whnf drainS (S.drop value)
      , bench "pipes" $ whnf drainP (P.drop value)
      , bench "conduit" $ whnf drainC (C.drop value)
      ]
  , bgroup "dropWhile"
      [ bench "machines" $ whnf drainM (M.droppingWhile (<= value))
      , bench "streaming" $ whnf drainS (S.dropWhile (<= value))
      , bench "pipes" $ whnf drainP (P.dropWhile (<= value))
      , bench "conduit" $ whnf drainC (CC.dropWhile (<= value))
      ]
  , bgroup "scan"
      [ bench "machines" $ whnf drainM (M.scan (+) 0)
      , bench "streaming" $ whnf drainS (S.scan (+) 0 id)
      , bench "pipes" $ whnf drainP (P.scan (+) 0 id)
      , bench "conduit" $ whnf drainC (CC.scanl (+) 0)
      ]
  , bgroup "take"
      [ bench "machines" $ whnf drainM (M.taking value)
      , bench "streaming" $ whnf drainS (S.take value)
      , bench "pipes" $ whnf drainP (P.take value)
      , bench "conduit" $ whnf drainC (C.isolate value)
      ]
  , bgroup "takeWhile"
      [ bench "machines" $ whnf drainM (M.takingWhile (<= value))
      , bench "streaming" $ whnf drainS (S.takeWhile (<= value))
      , bench "pipes" $ whnf drainP (P.takeWhile (<= value))
      , bench "conduit" $ whnf drainC (CC.takeWhile (<= value))
      ]
  , bgroup "fold"
      [ bench "machines" $ whnf drainM (M.fold (+) 0)
      , bench "streaming" $ whnf runIdentity $ (S.fold (+) 0 id) sourceS
      , bench "pipes" $ whnf runIdentity $ (P.fold (+) 0 id) sourceP
      , bench "conduit" $ whnf drainSC (C.fold (+) 0)
      ]
  , bgroup "filter"
      [ bench "machines" $ whnf drainM (M.filtered even)
      , bench "streaming" $ whnf drainS (S.filter even)
      , bench "pipes" $ whnf drainP (P.filter even)
      , bench "conduit" $ whnf drainC (C.filter even)
      ]
  , bgroup "mapM"
      [ bench "machines" $ whnf drainM (M.autoM Identity)
      , bench "streaming" $ whnf drainS (S.mapM Identity)
      , bench "pipes" $ whnf drainP (P.mapM Identity)
      , bench "conduit" $ whnf drainC (C.mapM Identity)
      ]
  , bgroup "zip"
      [ bench "machines" $ whnf (\x -> runIdentity $ M.runT_ x)
          (M.capT sourceM sourceM M.zipping)
      , bench "streaming" $ whnf (\x -> runIdentity $ S.effects $ x)
          (S.zip sourceS sourceS)
      , bench "pipes" $ whnf (\x -> runIdentity $ P.runEffect $ P.for x P.discard)
          (P.zip sourceP sourceP)
      , bench "conduit" $ whnf (\x -> runIdentity $ C.runConduit $ x C..| C.sinkNull)
          (C.getZipSource $ (,) <$> C.ZipSource sourceC <*> C.ZipSource sourceC)
      ]
  , bgroup "concat"
      [ bench "machines" $ whnf drainM (M.mapping (replicate 10) M.~> M.asParts)
      , bench "streaming" $ whnf drainS (S.concat . S.map (replicate 10))
      , bench "pipes" $ whnf drainP (P.map (replicate 10) P.>-> P.concat)
      , bench "conduit" $ whnf drainC (C.map (replicate 10) C..| C.concat)
      ]
  , bgroup "last"
      [ bench "machines" $ whnf drainM (M.final)
      , bench "streaming" $ whnf runIdentity $ S.last sourceS
      , bench "pipes" $ whnf runIdentity $ P.last sourceP
      ]
  , bgroup "buffered"
      [ bench "machines" $ whnf drainM (M.buffered 1000)
      ]
  , bgroup "toList"
      [ bench "machines"  $ whnf (length . runIdentity) $ M.runT sourceM
      , bench "streaming" $ whnf (length . runIdentity)
                          $ S.toList sourceS >>= (\(xs S.:> _) -> return xs)
      , bench "pipes"     $ whnf (length . runIdentity) $ P.toListM sourceP
      , bench "conduit"   $ whnf (length . runIdentity)
                          $ C.runConduit $ sourceC C..| CC.sinkList
      ]
  , bgroup "toListIO"
      [ bench "machines"  $ whnfIO $ M.runT sourceM
      , bench "streaming" $ whnfIO $ S.toList sourceS
      , bench "pipes"     $ whnfIO $ P.toListM sourceP
      , bench "conduit"   $ whnfIO $ C.runConduit $ sourceC C..| CC.sinkList
      ]

  , bgroup "compose"
      [
      -- Compose multiple ops, all stages letting everything through
        let m = M.filtered (<= value)
            s = S.filter (<= value)
            p = P.filter (<= value)
            c = C.filter (<= value)
        in bgroup "summary"
          [ bench "machines"  $ whnf drainM $ m M.~> m M.~> m M.~> m
          , bench "streaming" $ whnf drainS $ \x -> s x & s & s & s
          , bench "pipes"     $ whnf drainP $ p P.>-> p P.>-> p P.>-> p
          , bench "conduit"   $ whnf drainC $ c C..| c C..| c C..| c
          ]

      -- IO monad makes a big difference especially for machines
      , let m = M.filtered (<= value)
            s = S.filter (<= value)
            p = P.filter (<= value)
            c = C.filter (<= value)
        in bgroup "summary-io"
          [ bench "machines"  $ whnfIO $ drainMIO $ m M.~> m M.~> m M.~> m
          , bench "streaming" $ whnfIO $ drainSIO $ \x -> s x & s & s & s
          , bench "pipes"     $ whnfIO $ drainPIO $ p P.>-> p P.>-> p P.>-> p
          , bench "conduit"   $ whnfIO $ drainCIO $ c C..| c C..| c C..| c
          ]

      -- Scaling with same operation in sequence
      , let f = M.filtered (<= value)
        in bgroup "machines"
          [ bench "1-filter" $ whnf drainM f
          , bench "2-filters" $ whnf drainM $ f M.~> f
          , bench "3-filters" $ whnf drainM $ f M.~> f M.~> f
          , bench "4-filters" $ whnf drainM $ f M.~> f M.~> f M.~> f
          ]
      , let f = S.filter (<= value)
        in bgroup "streaming"
          [ bench "1-filter" $ whnf drainS (\x -> f x)
          , bench "2-filters" $ whnf drainS $ \x -> f x & f
          , bench "3-filters" $ whnf drainS $ \x -> f x & f & f
          , bench "4-filters" $ whnf drainS $ \x -> f x & f & f & f
          ]
      , let f = P.filter (<= value)
        in bgroup "pipes"
          [ bench "1-filter" $ whnf drainP f
          , bench "2-filters" $ whnf drainP $ f P.>-> f
          , bench "3-filters" $ whnf drainP $ f P.>-> f P.>-> f
          , bench "4-filters" $ whnf drainP $ f P.>-> f P.>-> f P.>-> f
          ]
      , let f = C.filter (<= value)
        in bgroup "conduit"
          [ bench "1-filter" $ whnf drainC f
          , bench "2-filters" $ whnf drainC $ f C..| f
          , bench "3-filters" $ whnf drainC $ f C..| f C..| f
          , bench "4-filters" $ whnf drainC $ f C..| f C..| f C..| f
          ]

      , let m = M.mapping (subtract 1) M.~> M.filtered (<= value)
            s = S.filter (<= value) . S.map (subtract 1)
            p = P.map (subtract 1)  P.>-> P.filter (<= value)
            c = C.map (subtract 1)  C..| C.filter (<= value)
        in bgroup "summary-alternate"
          [ bench "machines"  $ whnf drainM $ m M.~> m M.~> m M.~> m
          , bench "streaming" $ whnf drainS $ \x -> s x & s & s & s
          , bench "pipes"     $ whnf drainP $ p P.>-> p P.>-> p P.>-> p
          , bench "conduit"   $ whnf drainC $ c C..| c C..| c C..| c
          ]

      , let f = M.mapping (subtract 1) M.~> M.filtered (<= value)
        in bgroup "machines-alternate"
          [ bench "1-map-filter" $ whnf drainM f
          , bench "2-map-filters" $ whnf drainM $ f M.~> f
          , bench "3-map-filters" $ whnf drainM $ f M.~> f M.~> f
          , bench "4-map-filters" $ whnf drainM $ f M.~> f M.~> f M.~> f
          ]
      , let f = S.filter (<= value) . S.map (subtract 1)
        in bgroup "streaming-alternate"
          [ bench "1-map-filter" $ whnf drainS (\x -> f x)
          , bench "2-map-filters" $ whnf drainS $ \x -> f x & f
          , bench "3-map-filters" $ whnf drainS $ \x -> f x & f & f
          , bench "4-map-filters" $ whnf drainS $ \x -> f x & f & f & f
          ]
      , let f = P.map (subtract 1)  P.>-> P.filter (<= value)
        in bgroup "pipes-alternate"
          [ bench "1-map-filter" $ whnf drainP f
          , bench "2-map-filters" $ whnf drainP $ f P.>-> f
          , bench "3-map-filters" $ whnf drainP $ f P.>-> f P.>-> f
          , bench "4-map-filters" $ whnf drainP $ f P.>-> f P.>-> f P.>-> f
          ]
      , let f = C.map (subtract 1)  C..| C.filter (<= value)
        in bgroup "conduit-alternate"
          [ bench "1-map-filter" $ whnf drainC f
          , bench "2-map-filters" $ whnf drainC $ f C..| f
          , bench "3-map-filters" $ whnf drainC $ f C..| f C..| f
          , bench "4-map-filters" $ whnf drainC $ f C..| f C..| f C..| f
          ]

        -- how filtering affects the subsequent composition
      , let m = M.filtered (> value)
            s = S.filter   (> value)
            p = P.filter   (> value)
            c = C.filter   (> value)
        in bgroup "summary-filter-effect"
          [ bench "machines"  $ whnf drainM $ m M.~> m M.~> m M.~> m
          , bench "streaming" $ whnf drainS $ \x -> s x & s & s & s
          , bench "pipes"     $ whnf drainP $ p P.>-> p P.>-> p P.>-> p
          , bench "conduit"   $ whnf drainC $ c C..| c C..| c C..| c
          ]

      , let m = M.filtered (> value)
            s = S.filter   (> value)
            p = P.filter   (> value)
            c = C.filter   (> value)
        in bgroup "summary-filter-effect-io"
          [ bench "machines"  $ whnfIO $ drainMIO $ m M.~> m M.~> m M.~> m
          , bench "streaming" $ whnfIO $ drainSIO $ \x -> s x & s & s & s
          , bench "pipes"     $ whnfIO $ drainPIO $ p P.>-> p P.>-> p P.>-> p
          , bench "conduit"   $ whnfIO $ drainCIO $ c C..| c C..| c C..| c
          ]

      , let f = M.filtered (> value)
        in bgroup "machines-filter-effect"
          [ bench "filter1" $ whnf drainM f
          , bench "filter2" $ whnf drainM $ f M.~> f
          , bench "filter3" $ whnf drainM $ f M.~> f M.~> f
          , bench "filter4" $ whnf drainM $ f M.~> f M.~> f M.~> f
          ]
      , let f = S.filter (> value)
        in bgroup "streaming-filter-effect"
          [ bench "filter1" $ whnf drainS (\x -> f x)
          , bench "filter2" $ whnf drainS $ \x -> f x & f
          , bench "filter3" $ whnf drainS $ \x -> f x & f & f
          , bench "filter4" $ whnf drainS $ \x -> f x & f & f & f
          ]
      , let f = P.filter (> value)
        in bgroup "pipes-filter-effect"
          [ bench "filter1" $ whnf drainP f
          , bench "filter2" $ whnf drainP $ f P.>-> f
          , bench "filter3" $ whnf drainP $ f P.>-> f P.>-> f
          , bench "filter4" $ whnf drainP $ f P.>-> f P.>-> f P.>-> f
          ]
      , let f = C.filter (> value)
        in bgroup "conduit-filter-effect"
          [ bench "filter1" $ whnf drainC f
          , bench "filter2" $ whnf drainC $ f C..| f
          , bench "filter3" $ whnf drainC $ f C..| f C..| f
          , bench "filter4" $ whnf drainC $ f C..| f C..| f C..| f
          ]
      ]
  ]