packages feed

loop-0.3.0: bench/Bench.hs

{-# LANGUAGE BangPatterns #-}

module Main (main) where

import qualified Control.Foldl as Foldl
import           Control.Monad
import           Criterion.Main
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import           Data.Word
import           System.Random (randomIO)

import           Control.Loop (forLoop, numLoop)
import           Control.Loop.Internal (loop, unsafeLoop)


main :: IO ()
main = do

  r <- randomIO

  -- Warning: -fllvm can compile away the `unsafe` loops to complete no-ops.

  defaultMain
    [ bgroup "int" [ bench "loop"                         $ nfIO $ loop                         0 (1000000 :: Int)    (\_ -> return ())
                   , bench "unsafeLoop"                   $ nfIO $ unsafeLoop                   0 (1000000 :: Int)    (\_ -> return ())
                   , bench "numLoop"                      $ nfIO $ numLoop                      0 (1000000 :: Int)    (\_ -> return ())
                   , bench "forLoop"                      $ nfIO $ forLoop                      (0 :: Int) (< 1000000) (+1) (\_ -> return ())
                   , bench "loopLocal"                    $ nfIO $ loopLocal                    0 (1000000 :: Int)    (\_ -> return ())
                   , bench "unsafeLoopLocal"              $ nfIO $ unsafeLoopLocal              0 (1000000 :: Int)    (\_ -> return ())
                   , bench "loopMonad"                    $ nfIO $ loopMonad                    0 (1000000 :: Int)    (\_ -> return ())
                   , bench "numLoopMonadIntInlinable"     $ nfIO $ numLoopMonadIntInlinable     0 (1000000 :: Int)    (\_ -> return ())
                   , bench "numLoopMonadIntInline"        $ nfIO $ numLoopMonadIntInline        0 (1000000 :: Int)    (\_ -> return ())
                   , bench "numLoopInt"                   $ nfIO $ numLoopInt                   0 (1000000 :: Int)    (\_ -> return ())
                   , bench "numLoopEndInt"                $ nfIO $ numLoopEndInt                  (1000000 :: Int)    (\_ -> return ())
                   , bench "listForM_Int"                 $ nfIO $ listForM_Int                 0 (1000000 :: Int)    (\_ -> return ())
                   , bench "vectorFromListInt"            $ nfIO $ vectorFromListInt            0 (1000000 :: Int)    (\_ -> return ())
                   , bench "vectorEnumFromToInt"          $ nfIO $ vectorEnumFromToInt          0 (1000000 :: Int)    (\_ -> return ())
                -- Allocates the vector -> linear space and slow
                -- , bench "vectorEnumFromNInt"           $ nfIO $ vectorEnumFromNInt           0 (1000000 :: Int)    (\_ -> return ())
                   , bench "uvectorFromListInt"           $ nfIO $ uvectorFromListInt           0 (1000000 :: Int)    (\_ -> return ())
                   , bench "uvectorEnumFromToInt"         $ nfIO $ uvectorEnumFromToInt         0 (1000000 :: Int)    (\_ -> return ())
                -- Allocates the vector -> linear space and slow
                -- , bench "uvectorEnumFromNInt"          $ nfIO $ uvectorEnumFromNInt          0 (1000000 :: Int)    (\_ -> return ())
                   , bench "FoldL.FoldM"                  $ nfIO $ Foldl.foldM (Foldl.FoldM (\_m _x -> return ()) (return ()) return) [1..1000000::Int]
                   ]

    , bgroup "w32" [ bench "loop"                          $ nfIO $ loop                        0 (1000000 :: Word32) (\_ -> return ())
                   , bench "unsafeLoop"                    $ nfIO $ unsafeLoop                  0 (1000000 :: Word32) (\_ -> return ())
                   , bench "numLoop"                       $ nfIO $ numLoop                     0 (1000000 :: Word32) (\_ -> return ())
                   , bench "forLoop"                       $ nfIO $ forLoop                     (0 :: Word32) (< 1000000) (+1) (\_ -> return ())
                   , bench "loopLocal"                     $ nfIO $ loopLocal                   0 (1000000 :: Word32) (\_ -> return ())
                   , bench "unsafeLoopLocal"               $ nfIO $ unsafeLoopLocal             0 (1000000 :: Word32) (\_ -> return ())
                   , bench "loopMonad"                     $ nfIO $ loopMonad                   0 (1000000 :: Word32) (\_ -> return ())
                   , bench "numLoopW32"                    $ nfIO $ numLoopW32                  0 (1000000 :: Word32) (\_ -> return ())
                   , bench "numLoopEndW32"                 $ nfIO $ numLoopEndW32                 (1000000 :: Word32) (\_ -> return ())
                   , bench "listForM_W32"                  $ nfIO $ listForM_W32                0 (1000000 :: Word32) (\_ -> return ())
                   , bench "vectorFromListW32"             $ nfIO $ vectorFromListW32           0 (1000000 :: Word32) (\_ -> return ())
                   , bench "vectorEnumFromToW32"           $ nfIO $ vectorEnumFromToW32         0 (1000000 :: Word32) (\_ -> return ())
                   , bench "uvectorFromListW32"            $ nfIO $ uvectorFromListW32          0 (1000000 :: Word32) (\_ -> return ())
                   , bench "uvectorEnumFromToW32"          $ nfIO $ uvectorEnumFromToW32        0 (1000000 :: Word32) (\_ -> return ())
                   , bench "FoldL.FoldM"                   $ nfIO $ Foldl.foldM (Foldl.FoldM (\_m _x -> return ()) (return ()) return) [1..1000000::Word32]
                   ]

    -- `succ` is almost twice as slow as (+1) because it does an overflow check.
    -- This doesn't become apparent in this benchmark, only if you use it in a loop.
    , bgroup "inc" [ bench "+1"   $ whnf (+1) (r :: Int)
                   , bench "succ" $ whnf succ (r :: Int)
                   ]
    ]


-- Same as `loop` just defined locally.
loopLocal :: (Enum e, Eq e, Monad m) => e -> e -> (e -> m ()) -> m ()
loopLocal start end f = go start
  where
    go !x | x == end  = f x
          | otherwise = f x >> go (succ x)

{-# INLINEABLE loopLocal #-}


-- Same as `unsafeLoop` just defined locally.
unsafeLoopLocal :: (Enum e, Eq e, Monad m) => e -> e -> (e -> m ()) -> m ()
unsafeLoopLocal start end f = go start
  where
    go !x | x == end  = f x
          | otherwise = f x >> go (unsafeSucc x)
    unsafeSucc = toEnum . (+ 1) . fromEnum

{-# INLINEABLE unsafeLoopLocal #-}


-- Same as `loop` just with the monad monomorphic.
loopMonad :: (Enum e, Eq e) => e -> e -> (e -> IO ()) -> IO ()
loopMonad start end f = go start
  where
    go !x | x == end  = f x
          | otherwise = f x >> go (succ x)

{-# INLINEABLE loopMonad #-}


-- Same as `numLoop` just completely monomorphic, marked as INLINEABLE.
numLoopMonadIntInlinable :: Int -> Int -> (Int -> IO ()) -> IO ()
numLoopMonadIntInlinable start end f = go start
  where
    go !x | x == end  = f x
          | otherwise = f x >> go (x+1)

{-# INLINEABLE numLoopMonadIntInlinable #-}


-- Same as `numLoop` just completely monomorphic, marked as INLINE.
numLoopMonadIntInline :: Int -> Int -> (Int -> IO ()) -> IO ()
numLoopMonadIntInline start end f = go start
  where
    go !x | x == end  = f x
          | otherwise = f x >> go (x+1)

{-# INLINE numLoopMonadIntInline #-}


-- Same as `numLoop`, specialized to Int.
numLoopInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
numLoopInt start end f = go start
  where
    go !n | n == end  = f n >> return ()
          | otherwise = f n >> go (n+1)

{-# INLINEABLE numLoopInt #-}


-- Same as `numLoop`, specialized to Int with constant start value.
numLoopEndInt :: (Monad m) => Int -> (Int -> m ()) -> m ()
numLoopEndInt end f = go 0
  where
    go !n | n == end  = f n >> return ()
          | otherwise = f n >> go (n+1)

{-# INLINEABLE numLoopEndInt #-}


-- Same as `numLoop`, specialized to Int32.
numLoopW32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
numLoopW32 start end f = go start
  where
    go !n | n == end  = f n >> return ()
          | otherwise = f n >> go (n+1)

{-# INLINEABLE numLoopW32 #-}


-- Same as `numLoop`, specialized to Int32 with constant start value.
numLoopEndW32 :: (Monad m) => Word32 -> (Word32 -> m ()) -> m ()
numLoopEndW32 end f = go 0
  where
    go !n | n == end  = f n >> return ()
          | otherwise = f n >> go (n+1)

{-# INLINEABLE numLoopEndW32 #-}


-- Using `forM_`, specialized to Int.
listForM_Int :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
listForM_Int start end f = forM_ [start..end] f


-- Using `V.forM_` with `V.fromList`, specialized to Int.
vectorFromListInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
vectorFromListInt start end f = V.forM_ (V.fromList [start..end]) f


-- Using `V.forM_` with `V.enumFromTo`, specialized to Int.
vectorEnumFromToInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
vectorEnumFromToInt start end f = V.forM_ (V.enumFromTo start end) f


-- Using `V.forM_` with `V.enumFromN`, specialized to Int.
vectorEnumFromNInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
vectorEnumFromNInt start end f = V.forM_ (V.enumFromN start (end - start + 1)) f


-- Using `U.forM_` with `U.fromList`, specialized to Int.
uvectorFromListInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
uvectorFromListInt start end f = U.forM_ (U.fromList [start..end]) f


-- Using `U.forM_` with `U.enumFromTo`, specialized to Int.
uvectorEnumFromToInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
uvectorEnumFromToInt start end f = U.forM_ (U.enumFromTo start end) f


-- Using `U.forM_` with `U.enumFromN`, specialized to Int.
uvectorEnumFromNInt :: (Monad m) => Int -> Int -> (Int -> m ()) -> m ()
uvectorEnumFromNInt start end f = U.forM_ (U.enumFromN start (end - start + 1)) f


-- Using `forM_`, specialized to Word32.
listForM_W32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
listForM_W32 start end f = forM_ [start..end] f


-- Using `V.forM_` with `V.fromList`, specialized to Word32.
vectorFromListW32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
vectorFromListW32 start end f = V.forM_ (V.fromList [start..end]) f


-- Using `V.forM_` with `V.enumFromTo`, specialized to Word32.
vectorEnumFromToW32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
vectorEnumFromToW32 start end f = V.forM_ (V.enumFromTo start end) f


-- Using `U.forM_` with `U.fromList`, specialized to Word32.
uvectorFromListW32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
uvectorFromListW32 start end f = U.forM_ (U.fromList [start..end]) f


-- Using `U.forM_` with `U.enumFromTo`, specialized to Word32.
uvectorEnumFromToW32 :: (Monad m) => Word32 -> Word32 -> (Word32 -> m ()) -> m ()
uvectorEnumFromToW32 start end f = U.forM_ (U.enumFromTo start end) f