perf-0.8.0: src/Perf/Measure.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE StrictData #-}
{-# OPTIONS_GHC -Wall #-}
-- | Specification of a performance measurement type suitable for the 'PerfT' monad transformer.
module Perf.Measure
( Measure (..),
runMeasure,
runMeasureN,
cost,
cputime,
realtime,
count,
cycles,
)
where
import Control.Monad
import Data.Fixed (Fixed (MkFixed))
import Data.Time.Clock
import Perf.Cycle
import System.CPUTime
import System.CPUTime.Rdtsc
import Prelude
-- $setup
-- >>> import Data.Foldable (foldl')
-- | A Measure consists of a monadic effect prior to measuring, a monadic effect to finalise the measurement, and the value measured
--
-- For example, the measure specified below will return 1 every time measurement is requested, thus forming the base of a simple counter for loopy code.
--
-- >>> let count = Measure 0 (pure ()) (pure 1)
data Measure m b = forall a.
(Num b) =>
Measure
{ measure :: b,
prestep :: m a,
poststep :: a -> m b
}
-- | Measure a single effect.
--
-- >>> r <- runMeasure count (pure "joy")
-- >>> r
-- (1,"joy")
runMeasure :: Monad m => Measure m b -> m a -> m (b, a)
runMeasure (Measure _ pre post) a = do
p <- pre
!a' <- a
m' <- post p
return (m', a')
-- | Measure once, but run an effect multiple times.
--
-- >>> r <- runMeasureN 1000 count (pure "joys")
-- >>> r
-- (1,"joys")
runMeasureN :: Monad m => Int -> Measure m b -> m a -> m (b, a)
runMeasureN n (Measure _ pre post) a = do
p <- pre
replicateM_ (n - 1) a
!a' <- a
m' <- post p
return (m', a')
-- | cost of a measurement in terms of the Measure's own units
--
-- >>> r <- cost count
-- >>> r
-- 1
cost :: Monad m => Measure m b -> m b
cost (Measure _ pre post) = pre >>= post
-- | a measure using 'getCPUTime' from System.CPUTime (unit is picoseconds)
--
-- >>> r <- runMeasure cputime (pure $ foldl' (+) 0 [0..1000])
--
-- > (34000000,500500)
cputime :: Measure IO Integer
cputime = Measure 0 start stop
where
start = getCPUTime
stop a = do
t <- getCPUTime
return $ t - a
-- | a measure using 'getCurrentTime' (unit is seconds)
--
-- >>> r <- runMeasure realtime (pure $ foldl' (+) 0 [0..1000])
--
-- > (0.000046,500500)
realtime :: Measure IO Double
realtime = Measure m0 start stop
where
m0 = 0
start = getCurrentTime
stop a = do
t <- getCurrentTime
return $ fromNominalDiffTime $ diffUTCTime t a
fromNominalDiffTime :: NominalDiffTime -> Double
fromNominalDiffTime t = fromInteger i * 1e-12
where
(MkFixed i) = nominalDiffTimeToSeconds t
-- | a 'Measure' used to count iterations
--
-- >>> r <- runMeasure count (pure ())
-- >>> r
-- (1,())
count :: Measure IO Int
count = Measure m0 start stop
where
m0 = 0 :: Int
start = return ()
stop () = return 1
-- | a 'Measure' using the 'rdtsc' CPU register (units are in cycles)
--
-- >>> r <- runMeasureN 1000 cycles (pure ())
--
-- > (120540,()) -- ghci-level
-- > (18673,()) -- compiled with -O2
cycles :: Measure IO Cycle
cycles = Measure m0 start stop
where
m0 = 0
start = rdtsc
stop a = do
t <- rdtsc
return $ t - a