module Benchmark (
runBenchmark
) where
-- Control
import Control.Monad
import Control.DeepSeq
import Control.Exception
-- Data
import Data.Foldable (toList)
import Data.MultiChange (MultiChange)
import qualified Data.MultiChange as MultiChange
import Data.Incremental
-- System
import System.CPUTime
-- Test
import Test.QuickCheck
-- Utilities
import Utilities
{-FIXME:
Measure cumulative times. For doing so, perform first all recomputations and
then all adaptions. In each case, measure the start time and the time after
each step.
-}
-- * Benchmarking
runBenchmark :: (NFData a, Changeable a, NFData (DefaultChange a),
NFData b, Changeable b)
=> (a -> b)
-> (a ->> b)
-> (a, [DefaultChange a])
-> IO ()
runBenchmark fun trans src = do
timePairs <- benchmark fun trans src
zipWithM_ outputTimePair [0 ..] timePairs
data TimePair = TimePair {
recomputeTime :: Integer,
adaptTime :: Integer
}
benchmark :: (NFData a, Changeable a, NFData (DefaultChange a),
NFData b, Changeable b)
=> (a -> b)
-> (a ->> b)
-> (a, [DefaultChange a])
-> IO [TimePair]
benchmark fun trans src = do
fullyEvaluatedSrc <- fullyEvaluate src
let recomputeResults = recompute fun fullyEvaluatedSrc
let adaptResults = adapt trans fullyEvaluatedSrc
recomputeTimes <- getTimes recomputeResults
adaptTimes <- getTimes adaptResults
return $ zipWith TimePair recomputeTimes adaptTimes
getTimes :: NFData b => [b] -> IO [Integer]
getTimes results = do
startTime <- getCPUTime
let getTime result = do
fullyEvaluate result
finishTime <- getCPUTime
return (finishTime - startTime)
mapM getTime results
fullyEvaluate :: NFData a => a -> IO a
fullyEvaluate = evaluate . force
outputTimePair :: Int -> TimePair -> IO ()
outputTimePair stepNo timePair = putStrLn $
align stepNoWidth stepNo ++
": R " ++
timeOutput (recomputeTime timePair) ++
", A " ++
timeOutput (adaptTime timePair)
timeOutput :: Integer -> String
timeOutput time = align timeIntegralWidth (inCentiseconds `div` 100) ++
"." ++
show (fractionalPart `div` 10) ++
show (fractionalPart `mod` 10) ++
" s" where
inCentiseconds = (time + 5000000000) `div` 10000000000
fractionalPart = inCentiseconds `mod` 100
align :: Show a => Int -> a -> String
align width val = Prelude.reverse $
take width $
Prelude.reverse (show val) ++ repeat ' '
stepNoWidth :: Int
stepNoWidth = 8
timeIntegralWidth :: Int
timeIntegralWidth = 4
-- * Evaluation of changes
instance NFData a => NFData (PrimitiveChange a) where
rnf Keep = ()
rnf (ReplaceBy val) = val `deepseq` ()
instance NFData p => NFData (MultiChange p) where
rnf change = rnf (toList change)