debug-time-0.1.0.1: src/Debug/Time.hs
-- | Here's how you can use this package
--
-- @
-- module Main where
--
-- import Debug.Time
--
-- -- | Naive implementation
-- fibs :: [Int]
-- fibs = map (fibAt . maybeTimer) [1..]
-- where maybeTimer 10 = startTimer "10-30" 10
-- maybeTimer 30 = traceTimer "10-30" 30
-- maybeTimer 20 = startTimer "20-40" 20
-- maybeTimer 40 = traceTimer "20-40" 40
-- maybeTimer n = n
--
-- fibAt :: Int -> Int
-- fibAt 1 = 1
-- fibAt 2 = 1
-- fibAt n = fibAt (n - 1) + fibAt (n - 2)
--
-- main :: IO ()
-- main = do
-- initializeTimers
-- putStrLn "Calculating the first 40 fibonacci numbers while tracing the time elapsed between"
-- putStrLn "the computations 10-30 and 20-40"
-- mapM_ print (take 40 fibs)
-- @
{-# OPTIONS_GHC -fno-cse -fno-full-laziness #-}
module Debug.Time (startTimer, traceTimer, restartTimer, initializeTimers) where
import Data.Map (Map)
import qualified Data.Map as Map
import System.Clock (TimeSpec, timeSpecAsNanoSecs, getTime, diffTimeSpec, Clock(Monotonic))
import System.IO.Unsafe (unsafePerformIO)
import Data.IORef (IORef)
import qualified Data.IORef as Ref
import Debug.Trace (trace)
import Control.Monad (void)
import Numeric
{-# NOINLINE timers #-}
timers :: IORef (Map String TimeSpec)
timers = unsafePerformIO $! Ref.newIORef Map.empty
{-# NOINLINE readTimer #-}
readTimer :: String -> Integer
readTimer name = unsafePerformIO $ do
now <- getTime Monotonic
time <- Map.lookup name <$> Ref.readIORef timers
case time of
Nothing -> error $ "Attempting to read the timer '" ++ name ++ "' that has not been started"
Just t -> return $! timeSpecAsNanoSecs (diffTimeSpec now t)
-- | Initializes the timer store. This makes the first measurement more reliable.
initializeTimers :: IO ()
initializeTimers = void $! Ref.readIORef timers
-- | Ties the evaluation of the value with the start of a timer with the given name.
startTimer :: String -> a -> a
startTimer name x = start `seq` x where
start = unsafePerformIO $ do
time <- getTime Monotonic
Ref.modifyIORef' timers (Map.insert name time)
-- | Ties the evaluation of the value with an action tracing the elapsed time
-- since the start of the timer.
traceTimer :: String -> a -> a
traceTimer name = trace (format (readTimer name))
where format time = "Timer " ++ name ++ ": " ++ asSeconds time ++ " seconds."
asSeconds time = showFFloat Nothing (fromIntegral time / 1000000000 :: Double) ""
-- | Synonym for startTimer.
restartTimer :: String -> a -> a
restartTimer = startTimer