module Timing(
timed, timedIO,
startTimings,
printTimings
) where
import qualified Data.HashMap.Strict as Map
import Control.Exception
import Data.IORef.Extra
import Data.Tuple.Extra
import Data.List.Extra
import System.Time.Extra
import System.IO.Unsafe
type Category = String
type Item = String
useTimingsRef :: IORef Bool
useTimingsRef = unsafePerformIO $ newIORef False
useTimings :: Bool
useTimings = unsafePerformIO $ readIORef useTimingsRef
timings :: IORef (Map.HashMap (Category, Item) Seconds)
timings = unsafePerformIO $ newIORef Map.empty
timed :: Category -> Item -> a -> a
timed c i x = if not useTimings then x else unsafePerformIO $ timedIO c i $ evaluate x
timedIO :: Category -> Item -> IO a -> IO a
timedIO c i x = if not useTimings then x else do
(time, x) <- duration x
atomicModifyIORef' timings $ \mp -> (Map.insertWith (+) (c, i) time mp, ())
return x
startTimings :: IO ()
startTimings = do
writeIORef useTimingsRef True
writeIORef timings Map.empty
printTimings :: IO ()
printTimings = do
mp <- readIORef timings
let items = sortOn (sumSnd . snd) $
groupSort $ map (\((a,b),c) -> (a,(b,c))) $ Map.toList mp
putStrLn $ unlines $ intercalate [""] $ map disp $ items ++ [("TOTAL", map (second sumSnd) items)]
where
sumSnd = sum . map snd
disp (cat,xs) =
("Timing " ++ cat) :
[" " ++ showDuration b ++ " " ++ a | (a,b) <- xs2] ++
[" " ++ showDuration (sumSnd xs2) ++ " TOTAL"]
where
xs2 = f $ splitAt 9 $ sortOn (negate . snd) xs
f (xs,ys)
| length ys <= 1 = xs ++ ys
| otherwise = xs ++ [("Other items (" ++ show (length ys) ++ ")", sumSnd ys)]