packages feed

loop-0.2.0: bench/BenchFolds.hs

{-# LANGUAGE BangPatterns #-}

module Main (main) where

import           Control.Monad.State.Strict
import           Criterion.Main

import           Control.Loop (forLoop, forLoopFold, numLoopFold)


main :: IO ()
main = do

  defaultMain
    [ bgroup "pure"    [ bench "sumFold"           $ whnf sumFold          1000000
                       , bench "sumFoldTooStrict"  $ whnf sumFoldTooStrict 1000000
                       , bench "numSumFold"        $ whnf numSumFold       1000000
                       ]
    , bgroup "monadic" [ bench "sumFoldMonadic"    $ whnf sumFoldMonadic   1000000
                       ]
    ]


-- This is too strict if you want to write something like this;
--   forLoopFold  0 (<4) (+1) (error "default should not be evaluated") (\acc x -> x)
-- Namely it evaluates acc0 even if it is never used.
-- See http://neilmitchell.blogspot.co.uk/2013/08/destroying-performance-with-strictness.html
forLoopFoldTooStrict :: a -> (a -> Bool) -> (a -> a) -> acc -> (acc -> a -> acc) -> acc
forLoopFoldTooStrict start cond inc acc0 f = go acc0 start
  where
    go !acc !x | cond x    = go (f acc x) (inc x)
               | otherwise = acc

{-# INLINE forLoopFoldTooStrict #-}


sumFold :: Int -> Int
sumFold n = forLoopFold 0 (< n) (+1) 0 (+)

sumFoldTooStrict :: Int -> Int
sumFoldTooStrict n = forLoopFoldTooStrict 0 (< n) (+1) 0 (+)

sumFoldMonadic :: Int -> Int
sumFoldMonadic n = flip execState 0 $ do
  forLoop 0 (< n) (+1) $ \i -> do
    x <- get
    put $! x + i

numSumFold :: Int -> Int
numSumFold n = numLoopFold 0 (n - 1) 0 (+) -- numLoopFold is inclusive