packages feed

roguestar-gl-0.6.0.0: src/Statistics.hs

module Statistics
    (Statistics,newStatistics,runStatistics,
     animation_query,
     animation_post_exec)
    where

import Data.List as L
import Control.Concurrent
import RSAGL.FRP.Time
import System.IO
import System.IO.Unsafe
import RSAGL.Math.AbstractVector
import Control.Monad
import RSAGL.Math.Types
import Text.Printf
import Control.Exception
import Data.Maybe

{-# NOINLINE animation_query #-}
animation_query :: Statistics
animation_query = unsafePerformIO $ newStatistics "animation-query"

{-# NOINLINE animation_post_exec #-}
animation_post_exec :: Statistics
animation_post_exec = unsafePerformIO $ newStatistics "animation-post-exec"

{-# NOINLINE error_pump #-}
error_pump :: Chan String
error_pump = unsafePerformIO $
    do pump <- newChan
       _ <- forkIO $ forever $
                do hPutStrLn stderr =<< readChan pump
       return pump

data StatisticsBlock = StatisticsBlock {
    statistics_max :: !RSdouble,
    statistics_min :: !RSdouble,
    -- The running average.
    statistics_total :: !RSdouble,
    statistics_count :: !Integer }

empty_stats :: StatisticsBlock
empty_stats = StatisticsBlock {
    statistics_max = 0.0,
    statistics_min = 0.0,
    statistics_total = 0.0,
    statistics_count = 0 }

contributeSample :: RSdouble -> Maybe StatisticsBlock -> Maybe StatisticsBlock
contributeSample v Nothing = Just $ StatisticsBlock {
    statistics_max = v,
    statistics_min = v,
    statistics_total = v,
    statistics_count = 1 }
contributeSample v (Just stats) = Just $ stats {
    statistics_max = statistics_max stats `max` v,
    statistics_min = statistics_min stats `min` v,
    statistics_total = v + statistics_total stats,
    statistics_count = succ $ statistics_count stats }

data Statistics = Statistics {
    -- time since last flushing of statistics
    statistics_block :: MVar (Time,Maybe StatisticsBlock),
    statistics_name :: String }

newStatistics :: String -> IO Statistics
newStatistics name =
    do t <- getTime
       new_block <- newMVar (t,Nothing)
       return $ Statistics {
           statistics_block = new_block,
           statistics_name = name }

runStatistics :: Statistics -> IO a -> IO a
runStatistics stats_object action =
    do t_start <- getTime
       a <- action
       t_end <- getTime
       let t_diff = f2f $ toSeconds $ t_end `sub` t_start
       modifyMVar_ (statistics_block stats_object) $ \(t,m_stats) ->
           do let should_report = toSeconds (t_end `sub` t) > 60 && isJust m_stats
                  stats = fromMaybe empty_stats m_stats
              when should_report $
                  do let f = printf "%.4f" . (f2f :: RSdouble -> Double) :: RSdouble -> String
                     writeChan error_pump $ "\nSTATISTICS            " ++ statistics_name stats_object ++
                                            "\nMEAN                  " ++ f (statistics_total stats / fromInteger (statistics_count stats)) ++
                                            "\nMAX                   " ++ f (statistics_max stats) ++
                                            "\nMIN                   " ++ f (statistics_min stats) ++
                                            "\nN                     " ++ show (statistics_count stats) ++
                                            "\n% TIME                " ++ f (statistics_total stats / toSeconds (t_end `sub` t) * 100.0) ++
                                            "\nTHROUGHPUT            " ++ f (fromInteger (statistics_count stats) / statistics_total stats) ++
                                            "\nEVENTS PER SECOND     " ++ f (fromInteger (statistics_count stats) / toSeconds (t_end `sub` t))
              seq stats $ return $ if should_report then (t_end,Nothing) else (t,contributeSample t_diff m_stats)
       return a