packages feed

g2-0.2.0.0: src/G2/Data/Timer.hs

{-# LANGUAGE FlexibleInstances #-}

module G2.Data.Timer ( Timer
                     , TimerLog
                     , newTimer
                     , logEventStart
                     , logEventEnd
                     , getLog

                     , runTimer
                     , logEventStartM
                     , logEventEndM
                     , getLogM

                     , orderLogBySpeed
                     , sumLog
                     , mapLabels
                     , logToSecs ) where

import Control.Monad.IO.Class 
import Control.Monad.State.Lazy
import Data.Ord
import Data.List
import System.Clock

type TimerLog label = [(label, TimeSpec)]

data Timer label =
    Timer { timer_log :: TimerLog label -- ^ Labelled events with time measurements (in picoseconds)
          , for_next :: Maybe (label, TimeSpec) -- ^ What is the next label, and when did we start timing?
          }

newTimer :: IO (Timer label)
newTimer = do
    return $ Timer { timer_log = []
                   , for_next = Nothing } 

logEventStart :: label -> Timer label -> IO (Timer label)
logEventStart label timer = do
    curr <- getTime Realtime
    return $ logEventStart' label curr timer

logEventStart' :: label -> TimeSpec -> Timer label -> Timer label
logEventStart' label curr timer@( Timer { for_next = Nothing }) =
    timer { for_next = Just (label, curr) } 
logEventStart' _ _ _ = error "Timer started before ending"

logEventEnd :: Timer label -> IO (Timer label)
logEventEnd timer = do
    curr <- getTime Realtime
    return $ logEventEnd' curr timer

logEventEnd' :: TimeSpec -> Timer label -> Timer label
logEventEnd' curr (Timer { timer_log = lg, for_next = Just (label, lst) }) =
    Timer { timer_log = (label, curr - lst):lg
          , for_next = Nothing }
logEventEnd' _ _ = error "Timer ended but never started"

getLog :: Timer label -> TimerLog label
getLog = timer_log

runTimer :: StateT (Timer label) m a -> Timer label -> m (a, Timer label)
runTimer = runStateT

logEventStartM :: MonadIO m => label -> StateT (Timer label) m ()
logEventStartM n = do
    curr <- liftIO $ getTime Realtime
    modify' (logEventStart' n curr)

logEventEndM :: MonadIO m => StateT (Timer label) m ()
logEventEndM = do
    curr <- liftIO $ getTime Realtime
    modify' (logEventEnd' curr)

getLogM :: Monad m => StateT (Timer label) m (TimerLog label)
getLogM = gets getLog

-- Working with the generated logs
orderLogBySpeed :: TimerLog label -> TimerLog label
orderLogBySpeed = reverse . sortBy (comparing snd)

sumLog :: Eq label => TimerLog label -> TimerLog label
sumLog tl =
    let
        labs = nub $ map fst tl
        grped = map (sum . map snd)
              $ map (\l -> filter (\(l', _) -> l == l') tl) labs
    in
    zip labs grped

mapLabels :: (label1 -> label2) -> TimerLog label1 -> TimerLog label2
mapLabels f = map (\(l, i) -> (f l, i))

logToSecs :: TimerLog label -> [(label, Double)]
logToSecs = map (\(l, s) -> (l, fromInteger (toNanoSecs s) / (10 ^ (9 :: Int) :: Double)))