Blame benchmarks/optimize-201408.hs

Packit 368400
{-# LANGUAGE BangPatterns              #-}
Packit 368400
{-# LANGUAGE ExistentialQuantification #-}
Packit 368400
{-# LANGUAGE FlexibleContexts          #-}
Packit 368400
{-# LANGUAGE MultiParamTypeClasses     #-}
Packit 368400
{-# LANGUAGE TupleSections             #-}
Packit 368400
{-# LANGUAGE RankNTypes                #-}
Packit 368400
-- Collection of three benchmarks: a simple integral sum, monte carlo analysis,
Packit 368400
-- and sliding vector.
Packit 368400
import           Control.DeepSeq
Packit 368400
import           Control.Monad               (foldM)
Packit 368400
import           Control.Monad               (when, liftM)
Packit 368400
import           Control.Monad.Codensity     (lowerCodensity)
Packit 368400
import           Control.Monad.IO.Class      (MonadIO, liftIO)
Packit 368400
import           Control.Monad.Trans.Class   (lift)
Packit 368400
import           Criterion.Main
Packit 368400
import           Data.Conduit
Packit 368400
import           Data.Conduit.Internal       (ConduitM (..), Pipe (..))
Packit 368400
import qualified Data.Conduit.Internal       as CI
Packit 368400
import qualified Data.Conduit.List           as CL
Packit 368400
import qualified Data.Foldable               as F
Packit 368400
import           Data.Functor.Identity       (runIdentity)
Packit 368400
import           Data.IORef
Packit 368400
import           Data.List                   (foldl')
Packit 368400
import           Data.Monoid                 (mempty)
Packit 368400
import qualified Data.Sequence               as Seq
Packit 368400
import qualified Data.Vector                 as VB
Packit 368400
import qualified Data.Vector.Generic         as V
Packit 368400
import qualified Data.Vector.Generic.Mutable as VM
Packit 368400
import qualified Data.Vector.Unboxed         as VU
Packit 368400
import           System.Environment          (withArgs)
Packit 368400
import qualified System.Random.MWC           as MWC
Packit 368400
import           Test.Hspec
Packit 368400
Packit 368400
data TestBench = TBGroup String [TestBench]
Packit 368400
               | TBBench Benchmark
Packit 368400
               | forall a b. (Eq b, Show b) => TBPure String a b (a -> b)
Packit 368400
               | forall a. (Eq a, Show a) => TBIO String a (IO a)
Packit 368400
               | forall a. (Eq a, Show a) => TBIOTest String (a -> IO ()) (IO a)
Packit 368400
               | forall a. (Eq a, Show a) => TBIOBench String a (IO a) (IO ())
Packit 368400
Packit 368400
toSpec :: TestBench -> Spec
Packit 368400
toSpec (TBGroup name tbs) = describe name $ mapM_ toSpec tbs
Packit 368400
toSpec (TBBench _) = return ()
Packit 368400
toSpec (TBPure name a b f) = it name $ f a `shouldBe` b
Packit 368400
toSpec (TBIO name a f) = it name $ f >>= (`shouldBe` a)
Packit 368400
toSpec (TBIOTest name spec f) = it name $ f >>= spec
Packit 368400
toSpec (TBIOBench name a f _) = it name $ f >>= (`shouldBe` a)
Packit 368400
Packit 368400
toBench :: TestBench -> Benchmark
Packit 368400
toBench (TBGroup name tbs) = bgroup name $ map toBench tbs
Packit 368400
toBench (TBBench b) = b
Packit 368400
toBench (TBPure name a _ f) = bench name $ whnf f a
Packit 368400
toBench (TBIO name _ f) = bench name $ whnfIO f
Packit 368400
toBench (TBIOTest name _ f) = bench name $ whnfIO f
Packit 368400
toBench (TBIOBench name _ _ f) = bench name $ whnfIO f
Packit 368400
Packit 368400
runTestBench :: [TestBench] -> IO ()
Packit 368400
runTestBench tbs = do
Packit 368400
    withArgs [] $ hspec $ mapM_ toSpec tbs
Packit 368400
    defaultMain $ map toBench tbs
Packit 368400
Packit 368400
main :: IO ()
Packit 368400
main = runTestBench =<< sequence
Packit 368400
    [ sumTB
Packit 368400
    , mapSumTB
Packit 368400
    , monteCarloTB
Packit 368400
    , fmap (TBGroup "sliding window") $ sequence
Packit 368400
        [ slidingWindow 10
Packit 368400
        , slidingWindow 30
Packit 368400
        , slidingWindow 100
Packit 368400
        , slidingWindow 1000
Packit 368400
        ]
Packit 368400
    ]
Packit 368400
Packit 368400
-----------------------------------------------------------------------
Packit 368400
Packit 368400
sumTB :: IO TestBench
Packit 368400
sumTB = do
Packit 368400
    upperRef <- newIORef upper0
Packit 368400
    return $ TBGroup "sum"
Packit 368400
        [ TBPure "Data.List.foldl'" upper0 expected
Packit 368400
            $ \upper -> foldl' (+) 0 [1..upper]
Packit 368400
        , TBIO "Control.Monad.foldM" expected $ do
Packit 368400
            upper <- readIORef upperRef
Packit 368400
            foldM plusM 0 [1..upper]
Packit 368400
        , TBPure "low level" upper0 expected $ \upper ->
Packit 368400
            let go x !t
Packit 368400
                    | x > upper = t
Packit 368400
                    | otherwise = go (x + 1) (t + x)
Packit 368400
             in go 1 0
Packit 368400
        , TBIO "boxed vectors, I/O" expected $ do
Packit 368400
            upper <- readIORef upperRef
Packit 368400
            VB.foldM' plusM 0 $ VB.enumFromTo 1 upper
Packit 368400
        , TBPure "boxed vectors" upper0 expected
Packit 368400
            $ \upper -> VB.foldl' (+) 0 (VB.enumFromTo 1 upper)
Packit 368400
        , TBPure "unboxed vectors" upper0 expected
Packit 368400
            $ \upper -> VU.foldl' (+) 0 (VU.enumFromTo 1 upper)
Packit 368400
        , TBPure "conduit, pure, fold" upper0 expected
Packit 368400
            $ \upper -> runIdentity $ CL.enumFromTo 1 upper $$ CL.fold (+) 0
Packit 368400
        , TBPure "conduit, pure, foldM" upper0 expected
Packit 368400
            $ \upper -> runIdentity $ CL.enumFromTo 1 upper $$ CL.foldM plusM 0
Packit 368400
        , TBIO "conduit, IO, fold" expected $ do
Packit 368400
            upper <- readIORef upperRef
Packit 368400
            CL.enumFromTo 1 upper $$ CL.fold (+) 0
Packit 368400
        , TBIO "conduit, IO, foldM" expected $ do
Packit 368400
            upper <- readIORef upperRef
Packit 368400
            CL.enumFromTo 1 upper $$ CL.foldM plusM 0
Packit 368400
        ]
Packit 368400
  where
Packit 368400
    upper0 = 10000 :: Int
Packit 368400
    expected = sum [1..upper0]
Packit 368400
Packit 368400
    plusM x y = return $! x + y
Packit 368400
Packit 368400
-----------------------------------------------------------------------
Packit 368400
Packit 368400
mapSumTB :: IO TestBench
Packit 368400
mapSumTB = return $ TBGroup "map + sum"
Packit 368400
    [ TBPure "boxed vectors" upper0 expected
Packit 368400
        $ \upper -> VB.foldl' (+) 0
Packit 368400
                  $ VB.map (+ 1)
Packit 368400
                  $ VB.map (* 2)
Packit 368400
                  $ VB.enumFromTo 1 upper
Packit 368400
    , TBPure "unboxed vectors" upper0 expected
Packit 368400
        $ \upper -> VU.foldl' (+) 0
Packit 368400
                  $ VU.map (+ 1)
Packit 368400
                  $ VU.map (* 2)
Packit 368400
                  $ VU.enumFromTo 1 upper
Packit 368400
    , TBPure "conduit, connect1" upper0 expected $ \upper -> runIdentity
Packit 368400
        $ CL.enumFromTo 1 upper
Packit 368400
       $$ CL.map (* 2)
Packit 368400
      =$= CL.map (+ 1)
Packit 368400
      =$= CL.fold (+) 0
Packit 368400
    , TBPure "conduit, connect2" upper0 expected $ \upper -> runIdentity
Packit 368400
        $ CL.enumFromTo 1 upper
Packit 368400
      =$= CL.map (* 2)
Packit 368400
       $$ CL.map (+ 1)
Packit 368400
      =$= CL.fold (+) 0
Packit 368400
    , TBPure "conduit, connect3" upper0 expected $ \upper -> runIdentity
Packit 368400
        $ CL.enumFromTo 1 upper
Packit 368400
      =$= CL.map (* 2)
Packit 368400
      =$= CL.map (+ 1)
Packit 368400
       $$ CL.fold (+) 0
Packit 368400
    , TBPure "conduit, inner fuse" upper0 expected $ \upper -> runIdentity
Packit 368400
        $ CL.enumFromTo 1 upper
Packit 368400
      =$= (CL.map (* 2)
Packit 368400
      =$= CL.map (+ 1))
Packit 368400
       $$ CL.fold (+) 0
Packit 368400
    ]
Packit 368400
  where
Packit 368400
    upper0 = 10000 :: Int
Packit 368400
    expected = sum $ map (+ 1) $ map (* 2) [1..upper0]
Packit 368400
Packit 368400
-----------------------------------------------------------------------
Packit 368400
Packit 368400
monteCarloTB :: IO TestBench
Packit 368400
monteCarloTB = return $ TBGroup "monte carlo"
Packit 368400
    [ TBIOTest "conduit" closeEnough $ do
Packit 368400
        gen <- MWC.createSystemRandom
Packit 368400
        successes <- CL.replicateM count (MWC.uniform gen)
Packit 368400
                  $$ CL.fold (\t (x, y) ->
Packit 368400
                                if (x*x + y*(y :: Double) < 1)
Packit 368400
                                    then t + 1
Packit 368400
                                    else t)
Packit 368400
                        (0 :: Int)
Packit 368400
        return $ fromIntegral successes / fromIntegral count * 4
Packit 368400
    , TBIOTest "low level" closeEnough $ do
Packit 368400
        gen <- MWC.createSystemRandom
Packit 368400
        let go :: Int -> Int -> IO Double
Packit 368400
            go 0 !t = return $! fromIntegral t / fromIntegral count * 4
Packit 368400
            go i !t = do
Packit 368400
                (x, y) <- MWC.uniform gen
Packit 368400
                let t'
Packit 368400
                        | x*x + y*(y :: Double) < 1 = t + 1
Packit 368400
                        | otherwise = t
Packit 368400
                go (i - 1) t'
Packit 368400
        go count (0 :: Int)
Packit 368400
    ]
Packit 368400
  where
Packit 368400
    count = 100000 :: Int
Packit 368400
Packit 368400
    closeEnough x
Packit 368400
        | abs (x - 3.14159 :: Double) < 0.2 = return ()
Packit 368400
        | otherwise = error $ "Monte carlo analysis too inaccurate: " ++ show x
Packit 368400
Packit 368400
-----------------------------------------------------------------------
Packit 368400
Packit 368400
slidingWindow :: Int -> IO TestBench
Packit 368400
slidingWindow window = do
Packit 368400
    upperRef <- newIORef upper0
Packit 368400
    return $ TBGroup (show window)
Packit 368400
        [ TBIOBench "low level, Seq" expected
Packit 368400
            (swLowLevelSeq window upperRef id (\x y -> x . (F.toList y:)) ($ []))
Packit 368400
            (swLowLevelSeq window upperRef () (\() y -> rnf y) id)
Packit 368400
        , TBIOBench "conduit, Seq" expected
Packit 368400
            (swConduitSeq window upperRef id (\x y -> x . (F.toList y:)) ($ []))
Packit 368400
            (swConduitSeq window upperRef () (\() y -> rnf y) id)
Packit 368400
        {- https://ghc.haskell.org/trac/ghc/ticket/9446
Packit 368400
        , TBIOBench "low level, boxed Vector" expected
Packit 368400
            (swLowLevelVector window upperRef id (\x y -> x . (VB.toList y:)) ($ []))
Packit 368400
            (swLowLevelVector window upperRef () (\() y -> rnf (y :: VB.Vector Int)) id)
Packit 368400
            -}
Packit 368400
        , TBBench $ bench "low level, boxed Vector" $ whnfIO $
Packit 368400
            swLowLevelVector window upperRef () (\() y -> rnf (y :: VB.Vector Int)) id
Packit 368400
Packit 368400
        {- https://ghc.haskell.org/trac/ghc/ticket/9446
Packit 368400
        , TBIOBench "conduit, boxed Vector" expected
Packit 368400
            (swConduitVector window upperRef id (\x y -> x . (VB.toList y:)) ($ []))
Packit 368400
            (swConduitVector window upperRef () (\() y -> rnf (y :: VB.Vector Int)) id)
Packit 368400
        -}
Packit 368400
Packit 368400
        , TBBench $ bench "conduit, boxed Vector" $ whnfIO $
Packit 368400
            swConduitVector window upperRef () (\() y -> rnf (y :: VB.Vector Int)) id
Packit 368400
Packit 368400
Packit 368400
        , TBIOBench "low level, unboxed Vector" expected
Packit 368400
            (swLowLevelVector window upperRef id (\x y -> x . (VU.toList y:)) ($ []))
Packit 368400
            (swLowLevelVector window upperRef () (\() y -> rnf (y :: VU.Vector Int)) id)
Packit 368400
        , TBIOBench "conduit, unboxed Vector" expected
Packit 368400
            (swConduitVector window upperRef id (\x y -> x . (VU.toList y:)) ($ []))
Packit 368400
            (swConduitVector window upperRef () (\() y -> rnf (y :: VU.Vector Int)) id)
Packit 368400
        ]
Packit 368400
  where
Packit 368400
    upper0 = 10000
Packit 368400
    expected =
Packit 368400
        loop [1..upper0]
Packit 368400
      where
Packit 368400
        loop input
Packit 368400
            | length x == window = x : loop y
Packit 368400
            | otherwise = []
Packit 368400
          where
Packit 368400
            x = take window input
Packit 368400
            y = drop 1 input
Packit 368400
Packit 368400
swLowLevelSeq :: Int -> IORef Int -> t -> (t -> Seq.Seq Int -> t) -> (t -> t') -> IO t'
Packit 368400
swLowLevelSeq window upperRef t0 f final = do
Packit 368400
    upper <- readIORef upperRef
Packit 368400
Packit 368400
    let phase1 i !s
Packit 368400
            | i > window = phase2 i s t0
Packit 368400
            | otherwise = phase1 (i + 1) (s Seq.|> i)
Packit 368400
Packit 368400
        phase2 i !s !t
Packit 368400
            | i > upper = t'
Packit 368400
            | otherwise = phase2 (i + 1) s' t'
Packit 368400
          where
Packit 368400
            t' = f t s
Packit 368400
            s' = Seq.drop 1 s Seq.|> i
Packit 368400
Packit 368400
    return $! final $! phase1 1 mempty
Packit 368400
Packit 368400
swLowLevelVector :: V.Vector v Int
Packit 368400
                 => Int
Packit 368400
                 -> IORef Int
Packit 368400
                 -> t
Packit 368400
                 -> (t -> v Int -> t)
Packit 368400
                 -> (t -> t')
Packit 368400
                 -> IO t'
Packit 368400
swLowLevelVector window upperRef t0 f final = do
Packit 368400
    upper <- readIORef upperRef
Packit 368400
Packit 368400
    let go !i !t _ _ _ | i > upper = return $! final $! t
Packit 368400
        go !i !t !end _mv mv2 | end == bufSz  = newBuf >>= go i t sz mv2
Packit 368400
        go !i !t !end mv mv2 = do
Packit 368400
            VM.unsafeWrite mv end i
Packit 368400
            when (end > sz) $ VM.unsafeWrite mv2 (end - sz) i
Packit 368400
            let end' = end + 1
Packit 368400
            t' <-
Packit 368400
                if end' < sz
Packit 368400
                    then return t
Packit 368400
                    else do
Packit 368400
                        v <- V.unsafeFreeze $ VM.unsafeSlice (end' - sz) sz mv
Packit 368400
                        return $! f t v
Packit 368400
            go (i + 1) t' end' mv mv2
Packit 368400
Packit 368400
    mv <- newBuf
Packit 368400
    mv2 <- newBuf
Packit 368400
    go 1 t0 0 mv mv2
Packit 368400
  where
Packit 368400
    sz = window
Packit 368400
    bufSz = 2 * window
Packit 368400
    newBuf = VM.new bufSz
Packit 368400
Packit 368400
swConduitSeq :: Int
Packit 368400
             -> IORef Int
Packit 368400
             -> t
Packit 368400
             -> (t -> Seq.Seq Int -> t)
Packit 368400
             -> (t -> t')
Packit 368400
             -> IO t'
Packit 368400
swConduitSeq window upperRef t0 f final = do
Packit 368400
    upper <- readIORef upperRef
Packit 368400
Packit 368400
    t <- CL.enumFromTo 1 upper
Packit 368400
        $= slidingWindowC window
Packit 368400
        $$ CL.fold f t0
Packit 368400
    return $! final t
Packit 368400
Packit 368400
swConduitVector :: V.Vector v Int
Packit 368400
                => Int
Packit 368400
                -> IORef Int
Packit 368400
                -> t
Packit 368400
                -> (t -> v Int -> t)
Packit 368400
                -> (t -> t')
Packit 368400
                -> IO t'
Packit 368400
swConduitVector window upperRef t0 f final = do
Packit 368400
    upper <- readIORef upperRef
Packit 368400
Packit 368400
    t <- CL.enumFromTo 1 upper
Packit 368400
        $= slidingVectorC window
Packit 368400
        $$ CL.fold f t0
Packit 368400
    return $! final t
Packit 368400
Packit 368400
slidingWindowC :: Monad m => Int -> Conduit a m (Seq.Seq a)
Packit 368400
slidingWindowC = slidingWindowCC
Packit 368400
{-# INLINE [0] slidingWindowC #-}
Packit 368400
{-# RULES "unstream slidingWindowC"
Packit 368400
    forall i. slidingWindowC i = CI.unstream (CI.streamConduit (slidingWindowCC i) (slidingWindowS i))
Packit 368400
  #-}
Packit 368400
Packit 368400
slidingWindowCC :: Monad m => Int -> Conduit a m (Seq.Seq a)
Packit 368400
slidingWindowCC sz =
Packit 368400
    go sz mempty
Packit 368400
  where
Packit 368400
    goContinue st = await >>=
Packit 368400
                    maybe (return ())
Packit 368400
                          (\x -> do
Packit 368400
                             let st' = st Seq.|> x
Packit 368400
                             yield st' >> goContinue (Seq.drop 1 st')
Packit 368400
                          )
Packit 368400
    go 0 st = yield st >> goContinue (Seq.drop 1 st)
Packit 368400
    go !n st = CL.head >>= \m ->
Packit 368400
               case m of
Packit 368400
                 Nothing | n < sz -> yield st
Packit 368400
                         | otherwise -> return ()
Packit 368400
                 Just x -> go (n-1) (st Seq.|> x)
Packit 368400
{-# INLINE slidingWindowCC #-}
Packit 368400
Packit 368400
slidingWindowS :: Monad m => Int -> CI.Stream m a () -> CI.Stream m (Seq.Seq a) ()
Packit 368400
slidingWindowS sz (CI.Stream step ms0) =
Packit 368400
    CI.Stream step' $ liftM (\s -> Left (s, sz, mempty)) ms0
Packit 368400
  where
Packit 368400
    step' (Left (s, 0, st)) = return $ CI.Emit (Right (s, st)) st
Packit 368400
    step' (Left (s, i, st)) = do
Packit 368400
        res <- step s
Packit 368400
        return $ case res of
Packit 368400
            CI.Stop () -> CI.Stop ()
Packit 368400
            CI.Skip s' -> CI.Skip $ Left (s', i, st)
Packit 368400
            CI.Emit s' a -> CI.Skip $ Left (s', i - 1, st Seq.|> a)
Packit 368400
    step' (Right (s, st)) = do
Packit 368400
        res <- step s
Packit 368400
        return $ case res of
Packit 368400
            CI.Stop () -> CI.Stop ()
Packit 368400
            CI.Skip s' -> CI.Skip $ Right (s', st)
Packit 368400
            CI.Emit s' a ->
Packit 368400
                let st' = Seq.drop 1 st Seq.|> a
Packit 368400
                 in CI.Emit (Right (s', st')) st'
Packit 368400
{-# INLINE slidingWindowS #-}
Packit 368400
Packit 368400
slidingVectorC :: V.Vector v a => Int -> Conduit a IO (v a)
Packit 368400
slidingVectorC = slidingVectorCC
Packit 368400
{-# INLINE [0] slidingVectorC #-}
Packit 368400
{-# RULES "unstream slidingVectorC"
Packit 368400
    forall i. slidingVectorC i = CI.unstream (CI.streamConduit (slidingVectorCC i) (slidingVectorS i))
Packit 368400
  #-}
Packit 368400
Packit 368400
slidingVectorCC :: V.Vector v a => Int -> Conduit a IO (v a)
Packit 368400
slidingVectorCC sz = do
Packit 368400
    mv <- newBuf
Packit 368400
    mv2 <- newBuf
Packit 368400
    go 0 mv mv2
Packit 368400
  where
Packit 368400
    bufSz = 2 * sz
Packit 368400
    newBuf = liftIO (VM.new bufSz)
Packit 368400
Packit 368400
    go !end _mv mv2 | end == bufSz  = newBuf >>= go sz mv2
Packit 368400
    go !end mv mv2 = do
Packit 368400
      mx <- await
Packit 368400
      case mx of
Packit 368400
        Nothing -> when (end > 0 && end < sz) $ do
Packit 368400
          v <- liftIO $ V.unsafeFreeze $ VM.take end mv
Packit 368400
          yield v
Packit 368400
        Just x -> do
Packit 368400
          liftIO $ do
Packit 368400
            VM.unsafeWrite mv end x
Packit 368400
            when (end > sz) $ VM.unsafeWrite mv2 (end - sz) x
Packit 368400
          let end' = end + 1
Packit 368400
          when (end' >= sz) $ do
Packit 368400
            v <- liftIO $ V.unsafeFreeze $ VM.unsafeSlice (end' - sz) sz mv
Packit 368400
            yield v
Packit 368400
          go end' mv mv2
Packit 368400
Packit 368400
slidingVectorS :: V.Vector v a => Int -> CI.Stream IO a () -> CI.Stream IO (v a) ()
Packit 368400
slidingVectorS sz (CI.Stream step ms0) =
Packit 368400
    CI.Stream step' ms1
Packit 368400
  where
Packit 368400
    bufSz = 2 * sz
Packit 368400
    newBuf = liftIO (VM.new bufSz)
Packit 368400
Packit 368400
    ms1 = do
Packit 368400
        s <- ms0
Packit 368400
        mv <- newBuf
Packit 368400
        mv2 <- newBuf
Packit 368400
        return (s, 0, mv, mv2)
Packit 368400
Packit 368400
    step' (_, -1, _, _) = return $ CI.Stop ()
Packit 368400
    step' (s, end, _mv, mv2) | end == bufSz = do
Packit 368400
        mv3 <- newBuf
Packit 368400
        return $ CI.Skip (s, sz, mv2, mv3)
Packit 368400
    step' (s, end, mv, mv2) = do
Packit 368400
        res <- step s
Packit 368400
        case res of
Packit 368400
            CI.Stop ()
Packit 368400
                | end > 0 && end < sz -> do
Packit 368400
                    v <- liftIO $ V.unsafeFreeze $ VM.take end mv
Packit 368400
                    return $ CI.Emit (s, -1, mv, mv2) v
Packit 368400
                | otherwise -> return $ CI.Stop ()
Packit 368400
            CI.Skip s' -> return $ CI.Skip (s', end, mv, mv2)
Packit 368400
            CI.Emit s' x -> liftIO $ do
Packit 368400
                VM.unsafeWrite mv end x
Packit 368400
                when (end > sz) $ VM.unsafeWrite mv2 (end - sz) x
Packit 368400
                let end' = end + 1
Packit 368400
                    state = (s', end', mv, mv2)
Packit 368400
                if end' >= sz
Packit 368400
                    then do
Packit 368400
                        v <- V.unsafeFreeze $ VM.unsafeSlice (end' - sz) sz mv
Packit 368400
                        return $ CI.Emit state v
Packit 368400
                    else return $ CI.Skip state
Packit 368400
{-# INLINE slidingVectorS #-}