StrictBench-0.1: Test/StrictBench.hs
-- | A library to benchmark how long it takes to fully evaluate
-- a value. Can be useful to identify the slow part of an
-- algorithm, since Haskell's lazy evaluation can make it hard
-- to see where the bottleneck lies.
--
-- Full evalution of a value is achieved by the 'rnf' function,
-- which requires that the data type of the value being tested
-- is an instance of 'NFData'. Making a data type an instance
-- of 'NFData' is trivially done by applying 'rnf' to all of
-- its fields and 'seq'-ing those together.
--
-- Example:
--
-- > data Tree3 a = Leaf a | Branch (Tree3 a) (Tree3 a) (Tree3 a)
-- >
-- > instance NFData a => NFData (Tree3 a) where
-- > rnf (Leaf x) = rnf x
-- > rnf (Branch l c r) = rnf l `seq` rnf c `seq` rnf r
-- >
-- > main = bench . take 13 $ iterate (\x -> Branch x x x) (Leaf 'a')
-- >
-- > Output:
-- > 765.625 ms
--
-- If a data constructor has no fields you can suffice with (),
-- e.g.:
--
-- > data Answer = Yes | No
-- >
-- > instance NFData Answer where
-- > rnf Yes = ()
-- > rnf No = ()
module Test.StrictBench (bench, benchDesc, time) where
import Control.Parallel.Strategies
import Test.BenchPress hiding (bench)
import Text.Printf
-- | Print how long it takes to strictly evaluate the given
-- value.
--
-- Example:
--
-- > main = bench [1..10000000 :: Integer]
-- >
-- > Output:
-- > 515.625 ms
bench :: NFData a => a -> IO ()
bench = (putStrLn . (++ " ms") . show =<<) . time
-- | Like 'bench', benchDesc prints the time needed to fully
-- evaluate the given value. Additionally, it prefixes the time
-- taken with the provided string, which can be useful to
-- distinguish between different benchmarks.
--
-- Example:
--
-- > main = benchDesc "Long string" $ replicate 10000000 'a'
-- >
-- > Output:
-- > Long string: 375.0 ms
benchDesc :: NFData a => String -> a -> IO ()
benchDesc s = (putStrLn . printf "%s: %s ms" s . show =<<) . time
-- | The function used by bench and benchpress to determine how
-- long (in milliseconds) the value takes to calculate. You can
-- use this function for instance if you wish to sum the time
-- of several different values.
--
-- Example:
--
-- > main = do t1 <- time $ filter (< 10) $ take 1000000 $ repeat (9 :: Int)
-- > t2 <- time $ reverse $ take 1000000 $ cycle "StrictBench"
-- > print $ t1 + t2
-- >
-- > Output:
-- > 562.5
time :: NFData a => a -> IO Double
time = fmap (median . fst) . benchmark 1 (return ())
(const $ return ()) . const . putStr . (`seq` "") . rnf