{-# LANGUAGE CPP #-} module Criterion.Collection.Main ( CriterionCollectionConfig , defaultCriterionCollectionConfig , runBenchmark ) where import Control.DeepSeq import Control.Monad.Trans import Criterion.Collection.Sample import Criterion.Config import Criterion.Environment import Criterion.Measurement (secs) import Criterion.Monad import Data.List import System.IO import System.Random.MWC (GenIO) import qualified System.Random.MWC as R import Text.CSV #ifdef CHART import Criterion.Collection.Chart #endif data CriterionCollectionConfig = Cfg { _criterionConfig :: Config , _logPlot :: Bool -- todo: more here } defaultCriterionCollectionConfig :: CriterionCollectionConfig defaultCriterionCollectionConfig = Cfg defaultConfig False -- Fixme: fold chart output into config and generalize to other post-processing -- functions (like alternative chart types and CSV output) runBenchmark :: (NFData op) => MeasurementMode -> WorkloadMode -> Benchmark op -> CriterionCollectionConfig -> Maybe FilePath -> IO () runBenchmark mMode wMode benchmark (Cfg cfg logPlot) fp = withConfig cfg $ do rng <- liftIO $ R.withSystemRandom (\r -> return r :: IO GenIO) env <- measureEnvironment plotData <- takeSamples mMode wMode benchmark env rng liftIO $ mkChart logPlot (benchmarkName benchmark) fp plotData liftIO $ mkCSV (benchmarkName benchmark) fp plotData ------------------------------------------------------------------------------ mkCSV :: String -> Maybe FilePath -> [(String, [SampleData])] -> IO () mkCSV chartTitle output plotData = do h <- maybe (return stdout) (\f -> openFile (f ++ ".csv") WriteMode) output hPutStr h $ printCSV allRows maybe (return ()) (\_ -> hClose h) output where header = [ "Data Structure" , "Input Size" , "Mean (secs)" , "Stddev (secs)" , "95% (secs)" , "Max (secs)" ] allRows = header : sampleRows sampleRows = concatMap samplesToRows plotData samplesToRows (name, sds) = map (sampleToRow name) sds sampleToRow name sd = [ name , show inputSize , show mean , show stddev , show ninetyFifth , show maxVal ] where (mean, stddev) = computeMeanAndStddev sd ninetyFifth = compute95thPercentile sd maxVal = computeMax sd inputSize = sdInputSize sd ------------------------------------------------------------------------------ mkChart :: Bool -> String -> Maybe FilePath -> [(String, [SampleData])] -> IO () #ifdef CHART mkChart logPlot chartTitle output plotData' = do go output printChartResults chartTitle plotData' where plotData = map (\(a,(b,c)) -> (a,b,c)) (defaultColors `zip` plotData') go Nothing = do let chart = errBarChart logPlot 2.5 chartTitle plotData _ <- renderableToWindow chart 1024 768 return () go (Just fn) = do let chart = errBarChart logPlot 1.5 chartTitle plotData _ <- renderableToPNGFile chart 800 600 fn return () #else -- FIXME mkChart _ chartTitle _ plotData = printChartResults chartTitle plotData #endif ------------------------------------------------------------------------------ printChartResults :: String -> [(String, [SampleData])] -> IO () printChartResults chartTitle plotData = do -- fixme putStrLn $ "Results for " ++ chartTitle dashes crlf mapM_ printOne plotData where dashes = putStrLn $ replicate 78 '-' crlf = putStrLn "" fieldSize = 14 rpad s = if n > fieldSize then (take (fieldSize-2) s) ++ ".." else replicate nsp ' ' ++ s where n = length s nsp = fieldSize-n lpad s = if n > fieldSize then (take (fieldSize-2) s) ++ ".." else s ++ replicate nsp ' ' where n = length s nsp = fieldSize-n printHeader = do putStrLn $ concat [ lpad "Input Sz", " " , lpad "Mean (secs)", " " , lpad "Stddev (secs)", " " , lpad "95% (secs)", " " , lpad "Max (secs)"] putStrLn $ concat [ replicate fieldSize '-', " " , replicate fieldSize '-', " " , replicate fieldSize '-', " " , replicate fieldSize '-', " " , replicate fieldSize '-' ] printOne (name, sampledata) = do putStrLn $ "Data structure " ++ name crlf printHeader mapM_ printSample sampledata crlf printSample sd = do --putStrLn $ "fixme: sample length is " ++ show sd let (mean,stddev) = computeMeanAndStddev sd let ninetyFifth = compute95thPercentile sd let maxVal = computeMax sd let inputSize = sdInputSize sd let f1 = rpad $ show inputSize let f2 = rpad $ secs mean let f3 = rpad $ secs stddev let f4 = rpad $ secs ninetyFifth let f5 = rpad $ secs maxVal putStrLn $ intercalate " " [f1, f2, f3, f4, f5]