-- | Timing utilities used for benchmarks in the @repa-examples@ package.
module Data.Array.Repa.IO.Timing
        ( Time
        , milliseconds
        , microseconds
        , cpuTime, wallTime
        , time, minus, plus
        , showTime
        , prettyTime)
where
import System.CPUTime
import System.Time


-- Time -----------------------------------------------------------------------
-- | Abstract representation of process time.
data Time 
        = Time 
        { Time -> Integer
cpu_time  :: Integer
        , Time -> Integer
wall_time :: Integer
        }

zipT :: (Integer -> Integer -> Integer) -> Time -> Time -> Time
zipT :: (Integer -> Integer -> Integer) -> Time -> Time -> Time
zipT Integer -> Integer -> Integer
f (Time Integer
cpu1 Integer
wall1) (Time Integer
cpu2 Integer
wall2) 
        = Integer -> Integer -> Time
Time (Integer -> Integer -> Integer
f Integer
cpu1 Integer
cpu2) (Integer -> Integer -> Integer
f Integer
wall1 Integer
wall2)

-- | Subtract second time from the first.
minus :: Time -> Time -> Time
minus :: Time -> Time -> Time
minus = (Integer -> Integer -> Integer) -> Time -> Time -> Time
zipT (-)


-- | Add two times.
plus :: Time -> Time -> Time
plus :: Time -> Time -> Time
plus  = (Integer -> Integer -> Integer) -> Time -> Time -> Time
zipT Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(+)


-- TimeUnit -------------------------------------------------------------------
-- | Conversion 
type TimeUnit 
        = Integer -> Integer

microseconds :: TimeUnit 
microseconds :: Integer -> Integer
microseconds Integer
n = Integer
n Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
1000000

milliseconds :: TimeUnit
milliseconds :: Integer -> Integer
milliseconds Integer
n = Integer
n Integer -> Integer -> Integer
forall a. Integral a => a -> a -> a
`div` Integer
1000000000

cpuTime :: TimeUnit -> Time -> Integer
cpuTime :: (Integer -> Integer) -> Time -> Integer
cpuTime Integer -> Integer
f = Integer -> Integer
f (Integer -> Integer) -> (Time -> Integer) -> Time -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Time -> Integer
cpu_time

wallTime :: TimeUnit -> Time -> Integer
wallTime :: (Integer -> Integer) -> Time -> Integer
wallTime Integer -> Integer
f = Integer -> Integer
f (Integer -> Integer) -> (Time -> Integer) -> Time -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Time -> Integer
wall_time


-- | Get the current time.
getTime :: IO Time
getTime :: IO Time
getTime =
  do
    Integer
cpu          <- IO Integer
getCPUTime
    TOD Integer
sec Integer
pico <- IO ClockTime
getClockTime
    Time -> IO Time
forall (m :: * -> *) a. Monad m => a -> m a
return (Time -> IO Time) -> Time -> IO Time
forall a b. (a -> b) -> a -> b
$ Integer -> Integer -> Time
Time Integer
cpu (Integer
pico Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
+ Integer
sec Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
* Integer
1000000000000)


-- | Show a time as a string, in milliseconds.
showTime :: Time -> String
showTime :: Time -> String
showTime Time
t = (Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ (Integer -> Integer) -> Time -> Integer
wallTime Integer -> Integer
milliseconds Time
t)
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"/"
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ (Integer -> Integer) -> Time -> Integer
cpuTime  Integer -> Integer
milliseconds Time
t)

-- | Pretty print the times, in milliseconds.
prettyTime :: Time -> String
prettyTime :: Time -> String
prettyTime Time
t
        = [String] -> String
unlines
        [ String
"elapsedTimeMS   = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ (Integer -> Integer) -> Time -> Integer
wallTime Integer -> Integer
milliseconds Time
t)
        , String
"cpuTimeMS       = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (Integer -> String
forall a. Show a => a -> String
show (Integer -> String) -> Integer -> String
forall a b. (a -> b) -> a -> b
$ (Integer -> Integer) -> Time -> Integer
cpuTime  Integer -> Integer
milliseconds Time
t) ]

-- Timing benchmarks ----------------------------------------------------------

-- | Time some IO action.
--   Make sure to deepseq the result before returning it from the action. If you
--   don't do this then there's a good chance that you'll just pass a suspension
--   out of the action, and the computation time will be zero.
time :: IO a -> IO (a, Time)
{-# NOINLINE time #-}
time :: IO a -> IO (a, Time)
time IO a
p = do
           Time
start <- IO Time
getTime
           a
x     <- IO a
p
           Time
end   <- IO Time
getTime
           (a, Time) -> IO (a, Time)
forall (m :: * -> *) a. Monad m => a -> m a
return (a
x, Time
end Time -> Time -> Time
`minus` Time
start)