packages feed

buildbox 1.2.1.0 → 1.3.0.0

raw patch · 22 files changed

+1277/−419 lines, 22 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- BuildBox.Benchmark: BenchResult :: String -> [BenchRunResult] -> BenchResult
- BuildBox.Benchmark: BenchRunResult :: Float -> Maybe Timing -> BenchRunResult
- BuildBox.Benchmark: Timing :: Maybe Float -> Maybe Float -> Maybe Float -> Timing
- BuildBox.Benchmark: benchResultName :: BenchResult -> String
- BuildBox.Benchmark: benchResultRuns :: BenchResult -> [BenchRunResult]
- BuildBox.Benchmark: benchRunResultElapsed :: BenchRunResult -> Float
- BuildBox.Benchmark: benchRunResultKernel :: BenchRunResult -> Maybe Timing
- BuildBox.Benchmark: data BenchResult
- BuildBox.Benchmark: data BenchRunResult
- BuildBox.Benchmark: data Timing
- BuildBox.Benchmark: outRunBenchmarkAgainst :: Int -> Maybe BenchResult -> Benchmark -> Build BenchResult
- BuildBox.Benchmark: timingCpu :: Timing -> Maybe Float
- BuildBox.Benchmark: timingElapsed :: Timing -> Maybe Float
- BuildBox.Benchmark: timingSys :: Timing -> Maybe Float
- BuildBox.Benchmark.Compare: pprComparison :: BenchResult -> BenchResult -> Doc
- BuildBox.Benchmark.Compare: pprComparisons :: [BenchResult] -> [BenchResult] -> Doc
- BuildBox.Benchmark.Pretty: pprBenchResultAspect :: TimeAspect -> Maybe BenchResult -> BenchResult -> Maybe Doc
- BuildBox.Benchmark.Pretty: pprBenchResultAspectHeader :: Doc
- BuildBox.Benchmark.TimeAspect: TimeAspectElapsed :: TimeAspect
- BuildBox.Benchmark.TimeAspect: TimeAspectKernelCpu :: TimeAspect
- BuildBox.Benchmark.TimeAspect: TimeAspectKernelElapsed :: TimeAspect
- BuildBox.Benchmark.TimeAspect: TimeAspectKernelSys :: TimeAspect
- BuildBox.Benchmark.TimeAspect: data TimeAspect
- BuildBox.Benchmark.TimeAspect: instance Enum TimeAspect
- BuildBox.Benchmark.TimeAspect: instance Pretty TimeAspect
- BuildBox.Benchmark.TimeAspect: instance Read TimeAspect
- BuildBox.Benchmark.TimeAspect: instance Show TimeAspect
- BuildBox.Benchmark.TimeAspect: takeAvgTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float
- BuildBox.Benchmark.TimeAspect: takeMaxTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float
- BuildBox.Benchmark.TimeAspect: takeMinAvgMaxOfBenchResult :: TimeAspect -> BenchResult -> Maybe (Float, Float, Float)
- BuildBox.Benchmark.TimeAspect: takeMinTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float
- BuildBox.Benchmark.TimeAspect: takeTimeAspectOfBenchRunResult :: TimeAspect -> BenchRunResult -> Maybe Float
- BuildBox.IO.File: atomicWriteFile :: FilePath -> String -> IO ()
- BuildBox.Pretty: instance Pretty Doc
- BuildBox.Pretty: instance Pretty Float
- BuildBox.Pretty: instance Pretty Int
- BuildBox.Pretty: instance Pretty Integer
- BuildBox.Pretty: instance Pretty String
- BuildBox.Pretty: instance Pretty UTCTime
- BuildBox.Pretty: pprFloatRef :: Float -> Float -> Doc
- BuildBox.Pretty: pprFloatSR :: Float -> Doc
- BuildBox.Pretty: pprFloatTime :: Float -> Doc
- BuildBox.Pretty: pprPSecTime :: Integer -> Doc
+ BuildBox.Aspect: Size :: Sized -> c Bytes -> Aspect c Bytes
+ BuildBox.Aspect: Time :: Timed -> c Seconds -> Aspect c Seconds
+ BuildBox.Aspect: Used :: Used -> c Bytes -> Aspect c Bytes
+ BuildBox.Aspect: appAspect :: (Real units) => (c units -> b) -> Aspect c units -> b
+ BuildBox.Aspect: appAspectWithUnits :: (forall units. (Real units) => c units -> b) -> WithUnits (Aspect c) -> b
+ BuildBox.Aspect: data Aspect c :: (* -> *) units
+ BuildBox.Aspect: liftAspect :: (c1 units -> c2 units) -> Aspect c1 units -> Aspect c2 units
+ BuildBox.Aspect: liftAspect2 :: (c1 units -> c1 units -> c2 units) -> Aspect c1 units -> Aspect c1 units -> Aspect c2 units
+ BuildBox.Aspect: makeAspect :: (HasUnits (c units) units) => Detail -> c units -> Maybe (Aspect c units)
+ BuildBox.Aspect: makeAspectComparison :: (Real units) => [Aspect Stats units] -> Aspect Stats units -> Aspect StatsComparison units
+ BuildBox.Aspect: makeAspectComparisons :: (Real units) => [Aspect Stats units] -> [Aspect Stats units] -> [Aspect StatsComparison units]
+ BuildBox.Aspect: makeAspectStats :: Aspect [] units -> Aspect Stats units
+ BuildBox.Aspect: splitAspect :: Aspect c units -> (Detail, c units)
+ BuildBox.Aspect.Comparison: Comparison :: a -> a -> Double -> Comparison a
+ BuildBox.Aspect.Comparison: ComparisonNew :: a -> Comparison a
+ BuildBox.Aspect.Comparison: StatsComparison :: (Stats (Comparison a)) -> StatsComparison a
+ BuildBox.Aspect.Comparison: appSwing :: a -> (Double -> a) -> Comparison b -> a
+ BuildBox.Aspect.Comparison: comparisonBaseline :: Comparison a -> a
+ BuildBox.Aspect.Comparison: comparisonNew :: Comparison a -> a
+ BuildBox.Aspect.Comparison: comparisonRecent :: Comparison a -> a
+ BuildBox.Aspect.Comparison: comparisonSwing :: Comparison a -> Double
+ BuildBox.Aspect.Comparison: data Comparison a
+ BuildBox.Aspect.Comparison: data StatsComparison a
+ BuildBox.Aspect.Comparison: instance (Pretty a) => Pretty (Comparison a)
+ BuildBox.Aspect.Comparison: instance (Pretty a) => Pretty (StatsComparison a)
+ BuildBox.Aspect.Comparison: instance (Read a) => Read (Comparison a)
+ BuildBox.Aspect.Comparison: instance (Read a) => Read (StatsComparison a)
+ BuildBox.Aspect.Comparison: instance (Show a) => Show (Comparison a)
+ BuildBox.Aspect.Comparison: instance (Show a) => Show (StatsComparison a)
+ BuildBox.Aspect.Comparison: makeComparison :: (Real a) => a -> a -> Comparison a
+ BuildBox.Aspect.Comparison: makeStatsComparison :: (Real a) => Stats a -> Stats a -> StatsComparison a
+ BuildBox.Aspect.Comparison: makeStatsComparisonNew :: Stats a -> StatsComparison a
+ BuildBox.Aspect.Comparison: predSwingStatsComparison :: (Double -> Bool) -> StatsComparison a -> Bool
+ BuildBox.Aspect.Detail: DetailSized :: Sized -> Detail
+ BuildBox.Aspect.Detail: DetailTimed :: Timed -> Detail
+ BuildBox.Aspect.Detail: DetailUsed :: Used -> Detail
+ BuildBox.Aspect.Detail: ExeSize :: Sized
+ BuildBox.Aspect.Detail: HeapAlloc :: Used
+ BuildBox.Aspect.Detail: HeapMax :: Used
+ BuildBox.Aspect.Detail: KernelCpu :: Timed
+ BuildBox.Aspect.Detail: KernelSys :: Timed
+ BuildBox.Aspect.Detail: KernelWall :: Timed
+ BuildBox.Aspect.Detail: TotalCpu :: Timed
+ BuildBox.Aspect.Detail: TotalSys :: Timed
+ BuildBox.Aspect.Detail: TotalWall :: Timed
+ BuildBox.Aspect.Detail: data Detail
+ BuildBox.Aspect.Detail: data Sized
+ BuildBox.Aspect.Detail: data Timed
+ BuildBox.Aspect.Detail: data Used
+ BuildBox.Aspect.Detail: instance Enum Sized
+ BuildBox.Aspect.Detail: instance Enum Timed
+ BuildBox.Aspect.Detail: instance Enum Used
+ BuildBox.Aspect.Detail: instance Eq Detail
+ BuildBox.Aspect.Detail: instance Eq Sized
+ BuildBox.Aspect.Detail: instance Eq Timed
+ BuildBox.Aspect.Detail: instance Eq Used
+ BuildBox.Aspect.Detail: instance Ord Detail
+ BuildBox.Aspect.Detail: instance Ord Sized
+ BuildBox.Aspect.Detail: instance Ord Timed
+ BuildBox.Aspect.Detail: instance Ord Used
+ BuildBox.Aspect.Detail: instance Pretty Sized
+ BuildBox.Aspect.Detail: instance Pretty Timed
+ BuildBox.Aspect.Detail: instance Pretty Used
+ BuildBox.Aspect.Detail: instance Read Detail
+ BuildBox.Aspect.Detail: instance Read Sized
+ BuildBox.Aspect.Detail: instance Read Timed
+ BuildBox.Aspect.Detail: instance Read Used
+ BuildBox.Aspect.Detail: instance Show Detail
+ BuildBox.Aspect.Detail: instance Show Sized
+ BuildBox.Aspect.Detail: instance Show Timed
+ BuildBox.Aspect.Detail: instance Show Used
+ BuildBox.Aspect.Single: Single :: a -> Single a
+ BuildBox.Aspect.Single: data Single a
+ BuildBox.Aspect.Single: instance (Eq a) => Eq (Single a)
+ BuildBox.Aspect.Single: instance (Num a) => Num (Single a)
+ BuildBox.Aspect.Single: instance (Pretty a) => Pretty (Single a)
+ BuildBox.Aspect.Single: instance (Read a) => Read (Single a)
+ BuildBox.Aspect.Single: instance (Show a) => Show (Single a)
+ BuildBox.Aspect.Stats: Stats :: a -> a -> a -> Stats a
+ BuildBox.Aspect.Stats: data Stats a
+ BuildBox.Aspect.Stats: instance (Pretty a) => Pretty (Stats a)
+ BuildBox.Aspect.Stats: instance (Read a) => Read (Stats a)
+ BuildBox.Aspect.Stats: instance (Show a) => Show (Stats a)
+ BuildBox.Aspect.Stats: liftStats :: (a -> b) -> Stats a -> Stats b
+ BuildBox.Aspect.Stats: liftStats2 :: (a -> b -> c) -> Stats a -> Stats b -> Stats c
+ BuildBox.Aspect.Stats: makeStats :: (Real a, Dividable a) => [a] -> Stats a
+ BuildBox.Aspect.Stats: predStats :: (a -> Bool) -> Stats a -> Bool
+ BuildBox.Aspect.Stats: statsAvg :: Stats a -> a
+ BuildBox.Aspect.Stats: statsMax :: Stats a -> a
+ BuildBox.Aspect.Stats: statsMin :: Stats a -> a
+ BuildBox.Aspect.Units: Bytes :: Integer -> Bytes
+ BuildBox.Aspect.Units: IsBytes :: IsUnits Bytes
+ BuildBox.Aspect.Units: IsSeconds :: IsUnits Seconds
+ BuildBox.Aspect.Units: Seconds :: Double -> Seconds
+ BuildBox.Aspect.Units: WithBytes :: t Bytes -> WithUnits t
+ BuildBox.Aspect.Units: WithSeconds :: t Seconds -> WithUnits t
+ BuildBox.Aspect.Units: appWithUnits :: (forall units. (Real units) => t1 units -> b) -> WithUnits t1 -> b
+ BuildBox.Aspect.Units: bytes :: (Single Bytes -> c Single Bytes) -> Integer -> WithUnits (c Single)
+ BuildBox.Aspect.Units: class Collatable t
+ BuildBox.Aspect.Units: class HasUnits a b | a -> b
+ BuildBox.Aspect.Units: collate :: (Collatable t, HasUnits a a) => [t Single a] -> [t [] a]
+ BuildBox.Aspect.Units: collateWithUnits :: (Collatable c) => [WithUnits (c Single)] -> [WithUnits (c [])]
+ BuildBox.Aspect.Units: data Bytes
+ BuildBox.Aspect.Units: data IsUnits a
+ BuildBox.Aspect.Units: data Seconds
+ BuildBox.Aspect.Units: data WithUnits t
+ BuildBox.Aspect.Units: hasUnits :: (HasUnits a b) => a -> Maybe (IsUnits b)
+ BuildBox.Aspect.Units: instance (HasUnits a a) => HasUnits (Single a) a
+ BuildBox.Aspect.Units: instance (HasUnits a a) => HasUnits [a] a
+ BuildBox.Aspect.Units: instance (Pretty (t Bytes), Pretty (t Seconds)) => Pretty (WithUnits t)
+ BuildBox.Aspect.Units: instance (Read (t Bytes), Read (t Seconds)) => Read (WithUnits t)
+ BuildBox.Aspect.Units: instance (Show (t Bytes), Show (t Seconds)) => Show (WithUnits t)
+ BuildBox.Aspect.Units: instance Dividable Bytes
+ BuildBox.Aspect.Units: instance Dividable Seconds
+ BuildBox.Aspect.Units: instance Eq Bytes
+ BuildBox.Aspect.Units: instance Eq Seconds
+ BuildBox.Aspect.Units: instance HasUnits Bytes Bytes
+ BuildBox.Aspect.Units: instance HasUnits Seconds Seconds
+ BuildBox.Aspect.Units: instance Num Bytes
+ BuildBox.Aspect.Units: instance Num Seconds
+ BuildBox.Aspect.Units: instance Ord Bytes
+ BuildBox.Aspect.Units: instance Ord Seconds
+ BuildBox.Aspect.Units: instance Pretty Bytes
+ BuildBox.Aspect.Units: instance Pretty Seconds
+ BuildBox.Aspect.Units: instance Read Bytes
+ BuildBox.Aspect.Units: instance Read Seconds
+ BuildBox.Aspect.Units: instance Real Bytes
+ BuildBox.Aspect.Units: instance Real Seconds
+ BuildBox.Aspect.Units: instance Show Bytes
+ BuildBox.Aspect.Units: instance Show Seconds
+ BuildBox.Aspect.Units: instance Units Bytes
+ BuildBox.Aspect.Units: instance Units Seconds
+ BuildBox.Aspect.Units: liftWithUnits :: (forall units. (Real units) => t1 units -> t2 units) -> WithUnits t1 -> WithUnits t2
+ BuildBox.Aspect.Units: liftsWithUnits :: (forall units. (Real units) => [t1 units] -> [t2 units]) -> [WithUnits t1] -> [WithUnits t2]
+ BuildBox.Aspect.Units: liftsWithUnits2 :: (forall units. (Real units) => [t1 units] -> [t2 units] -> [t3 units]) -> [WithUnits t1] -> [WithUnits t2] -> [WithUnits t3]
+ BuildBox.Aspect.Units: secs :: (Single Seconds -> c Single Seconds) -> Double -> WithUnits (c Single)
+ BuildBox.Benchmark.BenchResult: BenchResult :: String -> [BenchRunResult c] -> BenchResult c
+ BuildBox.Benchmark.BenchResult: BenchRunResult :: Integer -> [WithUnits (Aspect c)] -> BenchRunResult c
+ BuildBox.Benchmark.BenchResult: appBenchRunResult :: ([BenchRunResult c1] -> b) -> BenchResult c1 -> b
+ BuildBox.Benchmark.BenchResult: appRunResultAspects :: ([WithUnits (Aspect c1)] -> b) -> BenchRunResult c1 -> b
+ BuildBox.Benchmark.BenchResult: benchResultName :: BenchResult c -> String
+ BuildBox.Benchmark.BenchResult: benchResultRuns :: BenchResult c -> [BenchRunResult c]
+ BuildBox.Benchmark.BenchResult: benchRunResultAspects :: BenchRunResult c -> [WithUnits (Aspect c)]
+ BuildBox.Benchmark.BenchResult: benchRunResultIndex :: BenchRunResult c -> Integer
+ BuildBox.Benchmark.BenchResult: collateBenchResult :: BenchResult Single -> BenchResult []
+ BuildBox.Benchmark.BenchResult: compareBenchResultWith :: [BenchResult Stats] -> BenchResult Stats -> BenchResult StatsComparison
+ BuildBox.Benchmark.BenchResult: compareBenchResults :: BenchResult Stats -> BenchResult Stats -> BenchResult StatsComparison
+ BuildBox.Benchmark.BenchResult: compareManyBenchResults :: [BenchResult Stats] -> [BenchResult Stats] -> [BenchResult StatsComparison]
+ BuildBox.Benchmark.BenchResult: concatBenchResult :: BenchResult c1 -> BenchResult c1
+ BuildBox.Benchmark.BenchResult: data BenchResult c
+ BuildBox.Benchmark.BenchResult: data BenchRunResult c
+ BuildBox.Benchmark.BenchResult: instance (HasUnits (c Bytes) Bytes, Read (c Bytes), HasUnits (c Seconds) Seconds, Read (c Seconds)) => Read (BenchResult c)
+ BuildBox.Benchmark.BenchResult: instance (HasUnits (c Bytes) Bytes, Read (c Bytes), HasUnits (c Seconds) Seconds, Read (c Seconds)) => Read (BenchRunResult c)
+ BuildBox.Benchmark.BenchResult: instance (Pretty (c Seconds), Pretty (c Bytes)) => Pretty (BenchResult c)
+ BuildBox.Benchmark.BenchResult: instance (Pretty (c Seconds), Pretty (c Bytes)) => Pretty (BenchRunResult c)
+ BuildBox.Benchmark.BenchResult: instance (Show (c Seconds), Show (c Bytes)) => Show (BenchResult c)
+ BuildBox.Benchmark.BenchResult: instance (Show (c Seconds), Show (c Bytes)) => Show (BenchRunResult c)
+ BuildBox.Benchmark.BenchResult: liftBenchRunResult :: ([BenchRunResult c1] -> [BenchRunResult c2]) -> (BenchResult c1 -> BenchResult c2)
+ BuildBox.Benchmark.BenchResult: liftBenchRunResult2 :: ([BenchRunResult c1] -> [BenchRunResult c2] -> [BenchRunResult c3]) -> BenchResult c1 -> BenchResult c2 -> BenchResult c3
+ BuildBox.Benchmark.BenchResult: liftRunResultAspects :: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)]) -> BenchRunResult c1 -> BenchRunResult c2
+ BuildBox.Benchmark.BenchResult: liftRunResultAspects2 :: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)] -> [WithUnits (Aspect c3)]) -> BenchRunResult c1 -> BenchRunResult c2 -> BenchRunResult c3
+ BuildBox.Benchmark.BenchResult: liftToAspectsOfBenchResult :: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)]) -> BenchResult c1 -> BenchResult c2
+ BuildBox.Benchmark.BenchResult: liftToAspectsOfBenchResult2 :: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)] -> [WithUnits (Aspect c3)]) -> BenchResult c1 -> BenchResult c2 -> BenchResult c3
+ BuildBox.Benchmark.BenchResult: predBenchResult :: (forall units. (Real units) => c units -> Bool) -> BenchResult c -> Bool
+ BuildBox.Benchmark.BenchResult: statBenchResult :: BenchResult Single -> BenchResult Stats
+ BuildBox.Benchmark.BenchResult: statCollatedBenchResult :: BenchResult [] -> BenchResult Stats
+ BuildBox.Benchmark.BenchResult: swungBenchResult :: Double -> BenchResult StatsComparison -> Bool
+ BuildBox.Data.Dividable: class Dividable a
+ BuildBox.Data.Dividable: divide :: (Dividable a) => a -> a -> a
+ BuildBox.Data.Dividable: instance Dividable Float
+ BuildBox.Data.Dividable: instance Dividable Integer
+ BuildBox.Pretty: instance [incoherent] (Pretty a) => Pretty [a]
+ BuildBox.Pretty: instance [incoherent] Pretty Doc
+ BuildBox.Pretty: instance [incoherent] Pretty Float
+ BuildBox.Pretty: instance [incoherent] Pretty Int
+ BuildBox.Pretty: instance [incoherent] Pretty Integer
+ BuildBox.Pretty: instance [incoherent] Pretty String
+ BuildBox.Pretty: instance [incoherent] Pretty UTCTime
+ BuildBox.Pretty: pprEngDouble :: String -> Double -> Maybe Doc
+ BuildBox.Pretty: pprEngInteger :: String -> Integer -> Maybe Doc
+ BuildBox.Reports.BenchResult: reportBenchResults :: Maybe Double -> [BenchResult StatsComparison] -> Doc
- BuildBox.Benchmark: Benchmark :: String -> Build () -> Build (Maybe Timing) -> Build () -> Benchmark
+ BuildBox.Benchmark: Benchmark :: String -> Build () -> Build [WithUnits (Aspect Single)] -> Build [WithUnits (Aspect Single)] -> Benchmark
- BuildBox.Benchmark: benchmarkCheck :: Benchmark -> Build ()
+ BuildBox.Benchmark: benchmarkCheck :: Benchmark -> Build [WithUnits (Aspect Single)]
- BuildBox.Benchmark: benchmarkCommand :: Benchmark -> Build (Maybe Timing)
+ BuildBox.Benchmark: benchmarkCommand :: Benchmark -> Build [WithUnits (Aspect Single)]
- BuildBox.Benchmark: outRunBenchmarkOnce :: Benchmark -> Build BenchRunResult
+ BuildBox.Benchmark: outRunBenchmarkOnce :: Integer -> Benchmark -> Build (BenchRunResult Single)
- BuildBox.Benchmark: outRunBenchmarkWith :: Int -> [BenchResult] -> Benchmark -> Build BenchResult
+ BuildBox.Benchmark: outRunBenchmarkWith :: Int -> [BenchResult Stats] -> Benchmark -> Build (BenchResult Single)
- BuildBox.Benchmark: runBenchmarkOnce :: Benchmark -> Build BenchRunResult
+ BuildBox.Benchmark: runBenchmarkOnce :: Integer -> Benchmark -> Build (BenchRunResult Single)
- BuildBox.FileFormat.BuildResults: BuildResults :: UTCTime -> Environment -> [BenchResult] -> BuildResults
+ BuildBox.FileFormat.BuildResults: BuildResults :: UTCTime -> Environment -> [BenchResult Single] -> BuildResults
- BuildBox.FileFormat.BuildResults: buildResultBench :: BuildResults -> [BenchResult]
+ BuildBox.FileFormat.BuildResults: buildResultBench :: BuildResults -> [BenchResult Single]

Files

BuildBox.hs view
@@ -1,8 +1,8 @@  module BuildBox-	( module BuildBox.Build+	( module BuildBox.Aspect 	, module BuildBox.Benchmark-	, module BuildBox.Pretty+	, module BuildBox.Build 	, module BuildBox.Command.Sleep 	, module BuildBox.Command.System 	, module BuildBox.Command.Network@@ -10,11 +10,15 @@ 	, module BuildBox.Command.Environment 	, module BuildBox.Command.File 	, module BuildBox.Cron+	, module BuildBox.FileFormat.BuildResults+	, module BuildBox.IO.Directory+	, module BuildBox.Pretty+	, module BuildBox.Reports.BenchResult 	, module BuildBox.Time) where-import BuildBox.Build+import BuildBox.Aspect import BuildBox.Benchmark-import BuildBox.Pretty+import BuildBox.Build import BuildBox.Command.Sleep import BuildBox.Command.System import BuildBox.Command.Network@@ -22,4 +26,8 @@ import BuildBox.Command.Environment import BuildBox.Command.File import BuildBox.Cron+import BuildBox.FileFormat.BuildResults+import BuildBox.IO.Directory+import BuildBox.Pretty+import BuildBox.Reports.BenchResult import BuildBox.Time
+ BuildBox/Aspect.hs view
@@ -0,0 +1,86 @@++-- | An aspect is a piece of data obtained from running a benchmark, like its +--   total runtime, heap usage, or executable size. Aspects have physical+--   units, so runtime is in seconds, and executable size is in bytes. The type system+--   ensures that you can't mess up the units, such as by treating executable size as though it was+--   measured in seconds.+--+--   Aspects are also parameterised over a carrier constructor, which is the collection type+--   used to store the data. For single valued data use the `Single` constructor. For multi valued+--   data use the @[]@ (the list constructor). Use this when you have several readings for+--   the same benchmark, like runtimes from multiple independent runs. +--   Other useful constructors are `Stats`, `Comparison` and `StatsComparison`.+--+--   Once you have a many-valued aspect, you can use `makeAspectStats` to compute statistics+--   from the data.+-- +--   Here is a worked example:+--+--  @ +-- -- This is our original, single valued data.+--someData :: [`WithUnits` (`Aspect` `Single`)]+--someData =  [ `Time` `TotalWall` \``secs`\`  100+--            , `Time` `TotalWall` \``secs`\`  85+--            , `Size` `ExeSize`   \``bytes`\` 1024+--            , `Used` `HeapMax`   \``bytes`\` 100000+--            , `Used` `HeapMax`   \``bytes`\` 100100]+--  @+--  +--  @+-- -- Collate the data, which groups all the readings for the same aspect into a list.+-- -- Note that the carrier constructor is now [].+--collated  :: [`WithUnits` (`Aspect` [])]+--collated  = `collateWithUnits` someData+-- ...+--show collated +--  =>   [ `WithSeconds` (`Time` `TotalWall` [`Seconds` 100.0, `Seconds` 85.0])+--       , `WithBytes`   (`Used` `HeapMax`   [`Bytes` 100000,  `Bytes` 100100])+--       , `WithBytes`   (`Size` `ExeSize`   [`Bytes` 1024])]+--  @+--+--  @+-- -- Extract statistics from the collated data.+--analysed   :: [`WithUnits` (`Aspect` `Stats`)]+--analysed   =  map (`liftWithUnits` `makeAspectStats`) collated+-- ...+--show analysed+--  =>   [ `WithSeconds` (`Time` `TotalWall` (`Stats` {`statsMin` = `Seconds` 85.0, `statsAvg` = `Seconds` 92.5, `statsMax` = `Seconds` 100.0}))+--       , `WithBytes`   (`Used` `HeapMax`   (`Stats` {`statsMin` = `Bytes` 100000, `statsAvg` = `Bytes` 100050, `statsMax` = `Bytes` 100100}))+--       , `WithBytes`   (`Size` `ExeSize`   (`Stats` {`statsMin` = `Bytes` 1024,   `statsAvg` = `Bytes` 1024,   `statsMax` = `Bytes` 1024}))]+-- @+module BuildBox.Aspect+	( module BuildBox.Aspect.Units+	, module BuildBox.Aspect.Detail+	, module BuildBox.Aspect.Stats+	, module BuildBox.Aspect.Single+	, module BuildBox.Aspect.Comparison++	-- * Aspects+	, Aspect	(..)+	, makeAspect+	, splitAspect++	-- * Statistics and comparisons+	, makeAspectStats+	, makeAspectComparison+	, makeAspectComparisons++	-- * Application functions+	, appAspect+	, appAspectWithUnits++	-- * Lifting functions+	, liftAspect+	, liftAspect2)+where+import BuildBox.Aspect.Aspect+import BuildBox.Aspect.Detail+import BuildBox.Aspect.Stats+import BuildBox.Aspect.Units+import BuildBox.Aspect.Single+import BuildBox.Aspect.Comparison+++++
+ BuildBox/Aspect/Aspect.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE 	ScopedTypeVariables, StandaloneDeriving,+		GADTs, FlexibleContexts, RankNTypes,+		UndecidableInstances, KindSignatures #-}+{-# OPTIONS_HADDOCK hide #-}+module BuildBox.Aspect.Aspect+	( Aspect	(..)+	, makeAspect+	, splitAspect+	, makeAspectStats+	, makeAspectComparison+	, makeAspectComparisons++	-- * Application functions+	, appAspect+	, appAspectWithUnits++	-- * Lifting functions+	, liftAspect+	, liftAspect2)+where+import BuildBox.Aspect.Single+import BuildBox.Aspect.Units+import BuildBox.Aspect.Detail+import BuildBox.Aspect.Stats+import BuildBox.Aspect.Comparison+import BuildBox.Pretty+import Text.Read+import Data.List+import qualified Data.Map	as Map+++-- | Holds a detail about a benchmark.+--+--   The @c@ is the type constructor of the carrier that holds the data.+--+--   Useful instances for @c@ include `Single`, `[ ]`, `Stats`, `Comparison` and `StatsComparison`.+--+data Aspect (c :: * -> *) units where+	Time	:: Timed	-> c Seconds	-> Aspect c Seconds+	Size	:: Sized	-> c Bytes	-> Aspect c Bytes+	Used	:: Used		-> c Bytes	-> Aspect c Bytes++deriving instance Show (c units) => Show (Aspect c units)	++-- We need to write the read instance manually because it requires makeAspect+instance (  HasUnits (c units) units+	 ,  Read  (c units)) +	 => Read (Aspect c units) where+ readPrec +  = do	tok <- lexP+	case tok of+	 Punc  "("+	  -> do	aspect		<- readPrec+		Punc ")"	<- lexP+		return aspect+		+	 Ident "Time" +	  -> do	timed		<- readPrec+		dat		<- readPrec+		let Just aspect	= makeAspect (DetailTimed timed) dat+		return aspect+	+	 Ident "Size"+	  -> do	sized		<- readPrec+		dat		<- readPrec+		let Just aspect	= makeAspect (DetailSized sized) dat+		return aspect++	 Ident "Used"+	  -> do	used		<- readPrec+		dat		<- readPrec+		let Just aspect	= makeAspect (DetailUsed used) dat+		return aspect+		+	 _ -> pfail+++instance ( Pretty (c Seconds)+	 , Pretty (c Bytes))+ 	 => Pretty (Aspect c units) where+ ppr aa+  = case aa of+	Time timed dat	-> padL 30 (ppr timed) <+> text ":" <+> ppr dat+	Size sized dat	-> padL 30 (ppr sized) <+> text ":" <+> ppr dat+	Used used  dat	-> padL 30 (ppr used)  <+> text ":" <+> ppr dat++	++-- | Split an aspect into its named detail and data.+splitAspect :: Aspect c units -> (Detail, c units)+splitAspect aa+ = case aa of+	Time timed val		-> (DetailTimed timed, val)+	Size sized val		-> (DetailSized sized, val)+	Used used  val		-> (DetailUsed  used,  val)+++-- | Make an aspect from a named detail and data.+--   If the detail doesn't match the units of the data then `Nothing`.+makeAspect+	:: HasUnits (c units) units +	=> Detail -> c units -> Maybe (Aspect c units)++makeAspect detail (val :: c units)+ = case hasUnits val :: Maybe (IsUnits units) of+	Just IsSeconds+	 -> case detail of+		DetailTimed timed	-> Just (Time timed val)+		_			-> Nothing++	Just IsBytes+	 -> case detail of+		DetailUsed  used	-> Just (Used used  val)+		DetailSized sized	-> Just (Size sized val)+		_			-> Nothing++	Nothing -> Nothing++++-- Collate ----------------------------------------------------------------------------------------+instance Collatable Aspect where+ collate as+  = let	-- This Just match will always succeed provided the implementation of gather is correct.+	Just as' = sequence +		 $ map (uncurry makeAspect) +		 $ gather [(detail, val) | (detail, (Single val)) <- map splitAspect as]+    in	as'++++-- | Gather a list of pairs on the first element+--	gather [(0, 1), (0, 2), (3, 2), (4, 5), (3, 1)] +--			= [(0, [1, 2]), (3, [2, 1]), (4, [5])]+gather :: Ord a => [(a, b)] -> [(a, [b])]+gather	xx	+ 	= Map.toList +	$ foldr (\(k, v) m -> +			Map.insertWith +				(\x xs -> x ++ xs) +				k [v] m) +		Map.empty +		xx+++-- Stats ------------------------------------------------------------------------------------------+-- | Compute statistics for many-valued aspects.+makeAspectStats :: Aspect [] units -> Aspect Stats units+makeAspectStats aspect+ = case aspect of+	Time timed dat	-> Time timed (makeStats dat)+	Size sized dat	-> Size sized (makeStats dat)+	Used used  dat	-> Used used  (makeStats dat)+++-- Comparison -------------------------------------------------------------------------------------+-- | Compare lists of aspects. The first argument is the baseline.+makeAspectComparisons +	:: Real units +	=> [Aspect Stats units] -> [Aspect Stats units] -> [Aspect StatsComparison units]+	+makeAspectComparisons base new+	= map (makeAspectComparison base) new+++-- | Lookup the baseline result for some aspect and produce a comparison.+makeAspectComparison+	:: Real units+	=> [Aspect Stats units] -> Aspect Stats units -> Aspect StatsComparison units++makeAspectComparison base aspect+ = case lookupAspect base aspect of+	Just aspectBase	-> liftAspect2 makeStatsComparison    aspectBase aspect+	Nothing		-> liftAspect  makeStatsComparisonNew aspect+++lookupAspect :: [Aspect Stats units] -> Aspect Stats units -> Maybe (Aspect Stats units)+lookupAspect base aspect+ = let	detail	= fst $ splitAspect aspect+   in	find (\a -> (fst $ splitAspect a) == detail) base++++-- Application ------------------------------------------------------------------------------------+-- | Apply a function to the data in an aspect+appAspect +	:: Real units +	=> (c units -> b) -> Aspect c units -> b++appAspect f aa = f (snd $ splitAspect aa)+	++-- | Apply a function to the data in a wrapped aspect.+appAspectWithUnits +	:: (forall units. Real units => c units -> b) +	-> WithUnits (Aspect c) -> b++appAspectWithUnits f+	= appWithUnits (appAspect f)++-- | Transform the data in an aspect, possibly changing the carrier type.+liftAspect +	:: (c1 units -> c2 units)+	-> Aspect c1 units -> Aspect c2 units++liftAspect f aspect+ = case aspect of+	Time timed dat	-> Time timed (f dat)+	Size sized dat	-> Size sized (f dat)+	Used used dat	-> Used used  (f dat)+++-- Lifting ----------------------------------------------------------------------------------------+-- | Apply a function to the aspect data, producing a new aspect.+--   If the aspect details don't match then `error`.+liftAspect2+	:: (c1 units -> c1 units -> c2 units) +	-> Aspect c1 units -> Aspect c1 units -> Aspect c2 units+	+liftAspect2 f a1 a2+ = case (a1, a2) of+	(Time timed1 dat1, Time timed2 dat2)+	 | timed1 == timed2	-> Time timed1 (f dat1 dat2)++	(Size sized1 dat1, Size sized2 dat2)+	 | sized1 == sized2	-> Size sized1 (f dat1 dat2)++	(Used used1 dat1,  Used used2 dat2)+	 | used1  == used2	-> Used used1 (f dat1 dat2)++	_ -> error "liftAspect2: aspects don't match"
+ BuildBox/Aspect/Comparison.hs view
@@ -0,0 +1,91 @@++module BuildBox.Aspect.Comparison+	( +	-- * Comparisons+	  Comparison	(..)+	, makeComparison+	, appSwing+	+	-- * Comparisons of Statistics+	, StatsComparison(..)+	, makeStatsComparison+	, makeStatsComparisonNew+	, predSwingStatsComparison)+where+import BuildBox.Aspect.Stats+import BuildBox.Pretty+import Text.Printf+++-- | The comparison of two values.+data Comparison a	+	-- | Comparison of a recent value with a baseline.+	= Comparison+	{ comparisonBaseline	:: a+	, comparisonRecent	:: a+	, comparisonSwing	:: Double }+	+	-- | A new value that doesn't have a baseline.+	| ComparisonNew+	{ comparisonNew		:: a }+	+	deriving (Read, Show)++instance Pretty a => Pretty (Comparison a) where+	ppr (Comparison _ recent ratio)+		| abs ratio < 0.01	+		= text $ printf "%s (----)"+				(render $ ppr recent)++		| otherwise		+		= text $ printf "%s (%+4.0f)"+				(render $ ppr recent)+				(ratio * 100)++	ppr (ComparisonNew new)+		= (padL 10 $ ppr new)+		++-- | Make a comparison from two values.+makeComparison :: Real a => a -> a -> Comparison a+makeComparison base recent+	= Comparison base recent swing+	+	where	dBase	= fromRational $ toRational base+		dRecent	= fromRational $ toRational recent+		swing = ((dRecent - dBase) / dBase)+++-- | Apply a function to the swing of a comparison.+appSwing :: a -> (Double -> a) -> Comparison b -> a+appSwing def f aa+ = case aa of+	Comparison _ _ swing	-> f swing+	ComparisonNew{}		-> def+	++-- StatsComparison --------------------------------------------------------------------------------+-- | Comparisons of statistics+data StatsComparison a+	= StatsComparison (Stats (Comparison a))+	deriving (Read, Show)++instance Pretty a => Pretty (StatsComparison a) where+	ppr (StatsComparison stats) = ppr stats++-- | Make a comparison of two `Stats`.+makeStatsComparison :: Real a => Stats a -> Stats a -> StatsComparison a+makeStatsComparison x y = StatsComparison (liftStats2 makeComparison x y)+	++-- | Make a `ComparisonNew`.+makeStatsComparisonNew :: Stats a -> StatsComparison a+makeStatsComparisonNew x+	= StatsComparison (liftStats ComparisonNew x)+	++-- | Return `True` if any of the swings in the `StatsComparison` match the given function.+predSwingStatsComparison :: (Double -> Bool) -> StatsComparison a -> Bool+predSwingStatsComparison f (StatsComparison ss)+	= (predStats . (appSwing False)) f ss+	
+ BuildBox/Aspect/Detail.hs view
@@ -0,0 +1,63 @@++-- | The detail is the name of an `Aspect` seprate from its data.+module BuildBox.Aspect.Detail+	( Detail (..)+	, Timed	 (..)+	, Used	 (..)+	, Sized	 (..))+where+import BuildBox.Pretty++data Detail+	= DetailTimed Timed+	| DetailUsed   Used+	| DetailSized  Sized+	deriving (Eq, Ord, Show, Read)+	++-- | Something that takes time to evaluate.+data Timed+	= TotalWall+	| TotalCpu+	| TotalSys+	| KernelWall+	| KernelCpu +	| KernelSys+	deriving (Eq, Ord, Show, Read, Enum)+	+instance Pretty Timed where+ ppr timed+  = case timed of+	TotalWall	-> text "runtime        (wall clock)"+	TotalCpu	-> text "runtime        (cpu usage)"+	TotalSys	-> text "runtime        (sys usage)"++	KernelWall	-> text "kernel runtime (wall clock)"+	KernelCpu	-> text "kernel runtime (cpu usage)"+	KernelSys	-> text "kernel runtime (sys usage)"+		++-- | Some resource used during execution.+data Used+	= HeapMax+	| HeapAlloc+	deriving (Eq, Ord, Show, Read, Enum)+	+instance Pretty Used where+ ppr used+  = case used of+	HeapMax		-> text "maximum heap usage"+	HeapAlloc	-> text "heap allocation"+	+	+-- | Some static size of the benchmark that isn't affected during the run.+data Sized+	= ExeSize+	deriving (Eq, Ord, Show, Read, Enum)+	+instance Pretty Sized where+ ppr sized+  = case sized of+	ExeSize		-> text "executable size"+	+	
+ BuildBox/Aspect/Single.hs view
@@ -0,0 +1,26 @@++module BuildBox.Aspect.Single+	( Single (..))+where +import BuildBox.Pretty++-- | A single valued piece of data.+data Single a +	= Single a+	deriving (Read, Show)++instance Num a => Num (Single a) where+	(+) (Single f1) (Single f2)	= Single (f1 + f2)+	(-) (Single f1) (Single f2)	= Single (f1 - f2)+	(*) (Single f1) (Single f2)	= Single (f1 * f2)+	abs (Single f1) 		= Single (abs f1)+	signum (Single f1)		= Single (signum f1)+	fromInteger i			= Single (fromInteger i)++instance Eq a => Eq (Single a) where+	(==) (Single f1) (Single f2)	= f1 == f2++instance Pretty a => Pretty (Single a) where+	ppr (Single x)	= ppr x++
+ BuildBox/Aspect/Stats.hs view
@@ -0,0 +1,54 @@++module BuildBox.Aspect.Stats+	( Stats	(..)+	, makeStats+	, predStats+	, liftStats+	, liftStats2)+where+import BuildBox.Pretty+import BuildBox.Data.Dividable+++-- | Statistics extracted from many-valued data.+data Stats a	+	= Stats+	{ statsMin	:: a+	, statsAvg	:: a+	, statsMax	:: a }+	deriving (Read, Show)++instance Pretty a => Pretty (Stats a) where+	ppr (Stats mi av mx)+		=   (ppr mi) <+> text "/" +		<+> (ppr av) <+> text "/"+		<+> (ppr mx)+++-- | Make statistics from a list of values.+makeStats :: (Real a, Dividable a) => [a] -> Stats a+makeStats xs+	= Stats (minimum xs)+		(sum xs `divide` (fromIntegral $ length xs)) +		(maximum xs)+++-- | Return `True` if the predicate matches any of the min, avg, max values.+predStats :: (a -> Bool) -> Stats a -> Bool+predStats f (Stats mi av mx) +	= or [f mi, f av, f mx]+++-- | Lift a function to each component of a `Stats`+liftStats :: (a -> b) -> Stats a -> Stats b+liftStats f (Stats mi av mx)+	= Stats (f mi) (f av) (f mx)+++-- | Lift a binary function to each component of a `Stats`+liftStats2 :: (a -> b -> c) -> Stats a -> Stats b -> Stats c+liftStats2 f (Stats min1 avg1 max1) (Stats min2 avg2 max2)+	= Stats (f min1 min2) (f avg1 avg2) (f max1 max2)+++	
+ BuildBox/Aspect/Units.hs view
@@ -0,0 +1,233 @@+{-# LANGUAGE StandaloneDeriving, GADTs, MultiParamTypeClasses, FunctionalDependencies, +	     FlexibleInstances,  RankNTypes, UndecidableInstances #-}++-- | Physical units of measure.+module BuildBox.Aspect.Units+	( +	  -- * The unit types+	  Seconds	(..)+	, Bytes		(..)++	  -- * IsUnits+	, IsUnits 	(..)++	  -- * HasUnits+	, HasUnits 	(..)+	+	  -- * WithUnits wrappers+	, WithUnits	(..)+	, secs+	, bytes+	, appWithUnits+	, liftWithUnits+	, liftsWithUnits+	, liftsWithUnits2++	  -- * Unit-preserving collation+	, Collatable	(..)+	, collateWithUnits)+	+where+import BuildBox.Aspect.Single+import BuildBox.Data.Dividable+import BuildBox.Pretty+import Data.Maybe+++-- Unit types -------------------------------------------------------------------------------------+-- | Seconds of time.+data Seconds	= Seconds Double+		deriving (Read, Show, Ord, Eq)++instance Real Seconds where+	toRational (Seconds s1) 	= toRational s1++instance Dividable Seconds where+	divide (Seconds s1) (Seconds s2) = Seconds (s1 / s2)	++instance Num Seconds where+	(+) (Seconds f1) (Seconds f2)	= Seconds (f1 + f2)+	(-) (Seconds f1) (Seconds f2)	= Seconds (f1 - f2)+	(*) (Seconds f1) (Seconds f2)	= Seconds (f1 * f2)+	abs (Seconds f1) 		= Seconds (abs f1)+	signum (Seconds f1)		= Seconds (signum f1)+	fromInteger i			= Seconds (fromInteger i)+	+instance Pretty Seconds where+	ppr (Seconds f)			+		= fromMaybe (text (show f))+		$ pprEngDouble "s" f+++-- | Bytes of data.+data Bytes	= Bytes	  Integer+		deriving (Read, Show, Ord, Eq)++instance Real Bytes where+	toRational (Bytes b1)		= toRational b1++instance Dividable Bytes where+	divide (Bytes s1) (Bytes s2)	= Bytes (s1 `div` s2)++instance Num Bytes where+	(+) (Bytes f1) (Bytes f2)	= Bytes (f1 + f2)+	(-) (Bytes f1) (Bytes f2)	= Bytes (f1 - f2)+	(*) (Bytes f1) (Bytes f2)	= Bytes (f1 * f2)+	abs (Bytes f1) 			= Bytes (abs f1)+	signum (Bytes f1)		= Bytes (signum f1)+	fromInteger i			= Bytes (fromInteger i)++instance Pretty Bytes where+	ppr (Bytes b)			+		= fromMaybe (text (show b))+		$ pprEngInteger "B" b+	++-- Type classes -----------------------------------------------------------------------------------+-- | Represents the units used for some thing.+data IsUnits a where+	IsSeconds 	:: IsUnits Seconds+	IsBytes		:: IsUnits Bytes	++class HasUnits a a => Units a where+	isUnits :: a -> Maybe (IsUnits a)++instance Units Seconds where+	isUnits s		= hasUnits s++instance Units Bytes where+	isUnits s		= hasUnits s+++-- | Determine the units used by the elements of some collection, +--   by inspecting the elements directly.+--   Returns `Nothing` when applied to empty collections, as they have no units.+class HasUnits a b | a -> b where+	hasUnits :: a -> Maybe (IsUnits b)++instance HasUnits Seconds Seconds where+	hasUnits _		= Just IsSeconds++instance HasUnits Bytes Bytes where+	hasUnits _		= Just IsBytes++instance HasUnits a a => HasUnits (Single a) a where+	hasUnits (Single x)	= hasUnits x++instance HasUnits a a => HasUnits [a] a where+	hasUnits []		= Nothing+	hasUnits (x : _)	= hasUnits x+++-- WithUnits --------------------------------------------------------------------------------------+-- | A wrapper type used to store data of varying physical units in a homogenous collection structure.+data WithUnits t where+	WithSeconds	:: t Seconds	-> WithUnits t+	WithBytes	:: t Bytes	-> WithUnits t+	+deriving instance (Show (t Bytes), Show (t Seconds)) => Show (WithUnits t)+deriving instance (Read (t Bytes), Read (t Seconds)) => Read (WithUnits t)++instance  (Pretty (t Bytes), Pretty (t Seconds))+	=> Pretty (WithUnits t) where+ ppr withUnits+  = case withUnits of+	WithSeconds s	-> ppr s+	WithBytes   b	-> ppr b++-- | Helpful wrapper for constructing seconds-valued aspect data. Examples:+-- +--   @Time TotalWall \`secs\` 10  ::  WithUnits (Aspect Single)@+-- +secs 	:: (Single Seconds -> c Single Seconds) +	-> Double -> WithUnits (c Single)+secs mk f  = WithSeconds (mk (Single (Seconds f)))+++-- | Similar to `secs`.+bytes 	:: (Single Bytes -> c Single Bytes) +	-> Integer -> WithUnits (c Single)+bytes mk b = WithBytes   (mk (Single (Bytes b)))+++-- | Apply a function to unit-wrapped data+appWithUnits+	:: (forall units. Real units => t1 units -> b)+	-> WithUnits t1 -> b+	+appWithUnits f withUnits+ = case withUnits of+	WithSeconds dat	-> f dat+	WithBytes   dat	-> f dat+++-- | Apply a function to unit-wrapped data.+liftWithUnits +	:: (forall units. Real units => t1 units -> t2 units)+	-> WithUnits t1 -> WithUnits t2++liftWithUnits f withUnits+ = case withUnits of+	WithSeconds dat	-> WithSeconds (f dat)+	WithBytes   dat -> WithBytes   (f dat)+++-- | Transform values of each unit type as a group.+liftsWithUnits +	:: (forall units. Real units => [t1 units] -> [t2 units]) +	-> [WithUnits t1] -> [WithUnits t2]++liftsWithUnits f us+  = let	asSeconds	= [a | WithSeconds a	<- us]+	asBytes		= [a | WithBytes   a	<- us]++    in	   (map WithSeconds $ f asSeconds)+	++ (map WithBytes   $ f asBytes)+	++-- | Transform values of each unit type as a group+liftsWithUnits2+	:: (forall units. Real units => [t1 units] -> [t2 units] -> [t3 units])+	-> [WithUnits t1] -> [WithUnits t2] -> [WithUnits t3]+	+liftsWithUnits2 f as bs+ = let	asSeconds	= [a | WithSeconds a	<- as]+	bsSeconds	= [b | WithSeconds b	<- bs]++	asBytes		= [a | WithBytes   a	<- as]+	bsBytes		= [b | WithBytes   b	<- bs]+	+   in	(map WithSeconds $ f asSeconds bsSeconds)+    ++	(map WithBytes   $ f asBytes   bsBytes)++++-- Unit-safe collation ----------------------------------------------------------------------------+-- | Collate some data, while preserving units.+class Collatable t where+	collate :: forall a. HasUnits a a +		=> [t Single a] -> [t [] a]+++-- | Collate some data.+--+--  @+-- collateWithUnits  [ Time KernelCpu \`secs\`  5+--                   , Time KernelCpu \`secs\`  10+--                   , Time TotalWall \`secs\`  55+--                   , Size ExeSize   \`bytes\` 100884+--                   , Time TotalWall \`secs\`  52 ]+-- =>+--                   [ WithSeconds (Time KernelCpu [Seconds 5.0,  Seconds 10.0])+--                   , WithSeconds (Time TotalWall [Seconds 55.0, Seconds 52.0])+--                   , WithBytes   (Size ExeSize [Bytes 1024])]+--  @+-- +collateWithUnits :: Collatable c => [WithUnits (c Single)] -> [WithUnits (c [])]+collateWithUnits as+  = let	asSeconds	= [a | WithSeconds a	<- as]+	asBytes		= [a | WithBytes   a	<- as]++    in	   (map WithSeconds $ collate asSeconds)+	++ (map WithBytes   $ collate asBytes)+
BuildBox/Benchmark.hs view
@@ -1,38 +1,18 @@-{-# LANGUAGE PatternGuards #-} --- | Running benchmarks and collecting timings. ------  These functions expect the given `Build` commands to succeed,---  throwing an error if they don't. If you're not sure whether your command will succeed then test it first. module BuildBox.Benchmark-	( module BuildBox.Benchmark.TimeAspect-	, module BuildBox.Benchmark.Pretty-	, module BuildBox.Benchmark.Compare-	-	-- * Types+	( module BuildBox.Benchmark.BenchResult 	, Benchmark(..)-	, Timing(..)-	, BenchRunResult(..)-	, BenchResult(..)-	-	-- * Benchmarking 	, runTimedCommand 	, runBenchmarkOnce 	, outRunBenchmarkOnce-	, outRunBenchmarkAgainst 	, outRunBenchmarkWith) where import BuildBox.Build	-import BuildBox.Pretty-import BuildBox.Benchmark.Base-import BuildBox.Benchmark.TimeAspect-import BuildBox.Benchmark.Pretty-import BuildBox.Benchmark.Compare+import BuildBox.Aspect+import BuildBox.Benchmark.Benchmark+import BuildBox.Benchmark.BenchResult import Data.Time-import Data.List-import Control.Monad - -- Running Commands ------------------------------------------------------------------------------- -- | Run a command, returning its elapsed time. runTimedCommand @@ -48,88 +28,65 @@  -- | Run a benchmark once. runBenchmarkOnce-	:: Benchmark -	-> Build BenchRunResult+	:: Integer		-- ^ Iteration number to tag results with.+	-> Benchmark 		-- ^ Benchmark to run.+	-> Build (BenchRunResult Single) 	-runBenchmarkOnce bench+runBenchmarkOnce iteration bench  = do	-- Run the setup command 	benchmarkSetup bench -	(diffTime, mKernelTimings)	+	(diffTime, asRun)	 		<- runTimedCommand  		$  benchmarkCommand bench 	-	benchmarkCheck bench+	asCheck	<- benchmarkCheck bench 	 	return	$ BenchRunResult-		{ benchRunResultElapsed		= fromRational $ toRational diffTime-		, benchRunResultKernel		= mKernelTimings }-+		{ benchRunResultIndex		= iteration +		-- Combine the aspects reported by the benchmark directly,+		-- also include our total runtime.+		, benchRunResultAspects		+			= Time TotalWall `secs` (fromRational $ toRational diffTime)+			: asRun ++ asCheck }+			+			 -- | Run a benchmark once, logging activity and timings to the console. outRunBenchmarkOnce-	:: Benchmark-	-> Build BenchRunResult+	:: Integer 		-- ^ Iteration number to tag results with+	-> Benchmark		-- ^ Benchmark to run.+	-> Build (BenchRunResult Single) 	-outRunBenchmarkOnce bench+outRunBenchmarkOnce iteration bench  = do	out $ "Running " ++ benchmarkName bench ++ "..."-	result	<- runBenchmarkOnce bench+	result	<- runBenchmarkOnce iteration bench 	outLn "ok"-	outLn $ text "    elapsed        = " <> (pprFloatTime $ benchRunResultElapsed result)-		-	maybe (return ()) (\t -> outLn $ text "    kernel elapsed = " <> pprFloatTime t) -		$ takeTimeAspectOfBenchRunResult TimeAspectKernelElapsed result--	maybe (return ()) (\t -> outLn $ text "    kernel cpu     = " <> pprFloatTime t) -		$ takeTimeAspectOfBenchRunResult TimeAspectKernelCpu result--	maybe (return ()) (\t -> outLn $ text "    kernel system  = " <> pprFloatTime t)-		$ takeTimeAspectOfBenchRunResult TimeAspectKernelSys result-	-	outBlank-	+	outLn result+	outBlank	 	return result ---- | Run a benchmark several times, logging activity to the console.---   Optionally print a comparison with a prior results.-outRunBenchmarkAgainst-	:: Int			-- ^ Number of iterations.-	-> Maybe BenchResult	-- ^ Optional previous result for comparison.-	-> Benchmark		-- ^ Benchmark to run.-	-> Build BenchResult 	-outRunBenchmarkAgainst iterations mPrior bench  +-- | Run a benchmark serveral times, logging activity to the console.+--   Also lookup prior results and print comparisons during the run.+outRunBenchmarkWith+	:: Int				-- ^ Number of times to run each benchmark to get averages.+	-> [BenchResult Stats]		-- ^ List of prior results.+	-> Benchmark			-- ^ The benchmark to run.+	-> Build (BenchResult Single)++outRunBenchmarkWith iterations priors bench  = do	out $ "Running " ++ benchmarkName bench ++ " " ++ show iterations ++ " times..."-	runResults	<- replicateM iterations (runBenchmarkOnce bench) +	runResults	<- mapM ((flip runBenchmarkOnce) bench) $ take iterations [1..] 	outLn "ok"  	let result	= BenchResult 			{ benchResultName	= benchmarkName bench 			, benchResultRuns	= runResults } -	outLn pprBenchResultAspectHeader+	outLn 	$ compareBenchResultWith priors +		$ statBenchResult result 	-	maybe (return ()) outLn	$ pprBenchResultAspect TimeAspectElapsed	mPrior result-	maybe (return ()) outLn	$ pprBenchResultAspect TimeAspectKernelElapsed	mPrior result-	maybe (return ()) outLn	$ pprBenchResultAspect TimeAspectKernelCpu	mPrior result-	maybe (return ()) outLn	$ pprBenchResultAspect TimeAspectKernelSys	mPrior result-		 	outBlank-	return	result----- | Run a benchmark serveral times, logging activity to the console.---   Also lookup prior results for comparison from the given list.---   If there is no matching entry then run the benchmark anyway, but don't print the comparison.-outRunBenchmarkWith-	:: Int			-- ^ Number of times to run each benchmark to get averages.-	-> [BenchResult]	-- ^ List of prior results.-	-> Benchmark		-- ^ The benchmark to run.-	-> Build BenchResult--outRunBenchmarkWith iterations priors bench- = let	mPrior	= find (\b -> benchResultName b == benchmarkName bench) priors-   in	outRunBenchmarkAgainst iterations mPrior bench-	+	return result 	
− BuildBox/Benchmark/Base.hs
@@ -1,78 +0,0 @@-{-# OPTIONS_HADDOCK hide #-}--module BuildBox.Benchmark.Base-	( Benchmark(..)-	, Timing(..)-	, BenchRunResult(..)-	, BenchResult(..))-where-import BuildBox.Build-import BuildBox.Pretty-import Control.Monad---- | Describes a benchmark that we can run.-data Benchmark-	= Benchmark-	{ -- | A unique name for the benchmark.-	  benchmarkName		:: String--	  -- | Setup command to run before the main benchmark.-	, benchmarkSetup	:: Build ()--	  -- | The benchmark command to run. Only the time taken to run this part is measured.-	, benchmarkCommand	:: Build (Maybe Timing)--	  -- | Check \/ cleanup command to run after the main benchmark.-	, benchmarkCheck	:: Build ()-	}----- | Holds elapsed, cpu, and system timings (in seconds).-data Timing-	= Timing-	{ timingElapsed	:: Maybe Float-	, timingCpu	:: Maybe Float-	, timingSys	:: Maybe Float }-	deriving (Eq, Read, Show)----- | The result of running a benchmark once.-data BenchRunResult-	= BenchRunResult--	{ -- | The wall-clock time taken to run the benchmark (in seconds)-	  benchRunResultElapsed		:: Float--	  -- | Time that the benchmark itself reported was taken to run its kernel.-	, benchRunResultKernel		:: Maybe Timing }--	deriving (Show, Read)---instance Pretty BenchRunResult where- ppr result-	= hang (ppr "BenchRunResult") 2 $ vcat-	[ ppr "elapsed:        " <> (ppr $ benchRunResultElapsed result) -	, maybe empty (\r -> ppr "k.elapsed: " <> ppr r) -		$ join $ liftM timingElapsed $ benchRunResultKernel result--	, maybe empty (\r -> ppr "k.cpu:     " <> ppr r)-		$ join $ liftM timingCpu     $ benchRunResultKernel result--	, maybe empty (\r -> ppr "k.system:  " <> ppr r)-		$ join $ liftM timingSys     $ benchRunResultKernel result ]-	-	--- | The result of running a benchmark several times.---   We include the name of the original benchmark to it's easy to lookup the results.-data BenchResult-	= BenchResult-	{ benchResultName	:: String-	, benchResultRuns	:: [BenchRunResult] }-	deriving (Show, Read)--instance Pretty BenchResult where- ppr result-	= hang (ppr "BenchResult") 2 $ vcat-	[ ppr $ benchResultName result-	, vcat $ map ppr $ benchResultRuns result ]
+ BuildBox/Benchmark/BenchResult.hs view
@@ -0,0 +1,252 @@+{-# LANGUAGE PatternGuards, StandaloneDeriving, FlexibleContexts, UndecidableInstances, RankNTypes #-}+module BuildBox.Benchmark.BenchResult+	( +	-- * Benchmark results	+	  BenchResult (..)++	-- * Concatenation+	, concatBenchResult+	+	-- * Collation+	, collateBenchResult++	-- * Statistics+	, statCollatedBenchResult+	, statBenchResult++	-- * Comparison+	, compareBenchResults+	, compareBenchResultWith+	, compareManyBenchResults+	, predBenchResult+	, swungBenchResult+	+	-- * Benchmark run results+	, BenchRunResult (..)++	-- * Application functions+	, appBenchRunResult+	, appRunResultAspects+++	-- * Lifting functions+	, liftBenchRunResult+	, liftBenchRunResult2+	, liftToAspectsOfBenchResult+	, liftToAspectsOfBenchResult2+	, liftRunResultAspects+	, liftRunResultAspects2)+where+import BuildBox.Aspect+import BuildBox.Pretty+import Data.List+++-- BenchResult ------------------------------------------------------------------------------------+-- | We include the name of the original benchmark to it's easy to lookup the results.+--   If the `BenchResult` is carrying data derived directly by running a benchmark, +--   there will be an element of the `benchResultRuns` for each iteration. On the other hand,+--   If the `BenchResult` is carrying statistics or comparison data there should+--   be a single element with an index of 0. This is suggested usage, and  adhered to by+--   the functions in this module, but not required.+data BenchResult c+	= BenchResult+	{ benchResultName	:: String+	, benchResultRuns	:: [BenchRunResult c] }++deriving instance +	(  Show (c Seconds), Show (c Bytes)) +	=> Show (BenchResult c)++deriving instance+	(  HasUnits (c Bytes) Bytes+	,  Read (c Bytes)+	,  HasUnits (c Seconds) Seconds+	,  Read (c Seconds))+	=> Read (BenchResult c)+++instance  ( Pretty (c Seconds), Pretty (c Bytes))+	 => Pretty (BenchResult c) where+ ppr result+	= text (benchResultName result)+	$+$ nest 4 (vcat $ map ppr $ benchResultRuns result)+++-- | Concatenate the results of all runs.+--   The the resulting `BenchResult` has a single `BenchRunResult` with an index of 0, containing all aspects.+concatBenchResult :: BenchResult c1 -> BenchResult c1+concatBenchResult +	= liftBenchRunResult +	$ \bsResults -> [BenchRunResult 0 (concatMap benchRunResultAspects bsResults)]+++-- | Collate the aspects of each run. See `collateWithUnits` for an explanation and example.+collateBenchResult :: BenchResult Single -> BenchResult []+collateBenchResult+	= liftToAspectsOfBenchResult collateWithUnits+++-- | Compute statistics from collated aspects of a run.+statCollatedBenchResult :: BenchResult [] -> BenchResult Stats+statCollatedBenchResult+	= liftToAspectsOfBenchResult (map (liftWithUnits makeAspectStats))+++-- | Collate the aspects, then compute statistics of a run.+statBenchResult :: BenchResult Single -> BenchResult Stats+statBenchResult +	= statCollatedBenchResult . collateBenchResult . concatBenchResult+++-- | Compute comparisons of benchmark results.+--	Both results must have the same `benchResultName` else `error`.+compareBenchResults+	:: BenchResult Stats -> BenchResult Stats -> BenchResult StatsComparison++compareBenchResults +	= liftBenchRunResult2 (zipWith (liftRunResultAspects2 (liftsWithUnits2 makeAspectComparisons)))+++-- | Compute comparisons of benchmark result, looking up the baseline results from a given list.+--	If there are no matching baseline results then this creates a `ComparisonNew` in the output.+compareBenchResultWith +	:: [BenchResult Stats] -> BenchResult Stats -> BenchResult StatsComparison++compareBenchResultWith base result+	| Just baseResult	<- find (\baseResult -> benchResultName baseResult == benchResultName result) base+	= compareBenchResults baseResult result+	+	| otherwise+	= liftToAspectsOfBenchResult (liftsWithUnits (map (liftAspect makeStatsComparisonNew))) result+++-- | Compare some baseline results against new results.+--	If there are no matching baseline results then this creates a `ComparisonNew` in the output.+compareManyBenchResults +	:: [BenchResult Stats] -> [BenchResult Stats] -> [BenchResult StatsComparison]+	+compareManyBenchResults base new+	= map (compareBenchResultWith base) new+++-- | Return true if any of the aspect data in a result matches a given predicate.+predBenchResult+	:: (forall units. Real units => c units -> Bool)+	-> BenchResult c -> Bool++predBenchResult f+	= appBenchRunResult $ or . map (appRunResultAspects $ or . map (appAspectWithUnits f))+++-- | Return true if any of the aspects have swung by more than a given fraction since last time.+--   For example, use @0.1@ for 10 percent.+swungBenchResult :: Double -> BenchResult StatsComparison -> Bool+swungBenchResult limit+	= predBenchResult (predSwingStatsComparison (\x -> abs x > limit)) +++-- Lifting ----------------------------------------------------------------------------------------+-- | Apply a function to the aspects of a `BenchRunResult`+appBenchRunResult :: ([BenchRunResult c1] -> b) -> BenchResult c1 -> b+appBenchRunResult f (BenchResult _ runs) = f runs+++-- | Lift a function to the `BenchRunResult` in a `BenchResult`+liftBenchRunResult +	:: ([BenchRunResult c1] -> [BenchRunResult  c2])+	-> (BenchResult     c1  -> BenchResult      c2)++liftBenchRunResult f (BenchResult name runs)	+	= BenchResult name (f runs)+++-- | Lift a binary function to the `BenchResults` in a `BenchResult`+liftBenchRunResult2+	:: ([BenchRunResult c1] -> [BenchRunResult c2] -> [BenchRunResult c3])+	->  BenchResult c1      ->  BenchResult c2     ->  BenchResult c3++liftBenchRunResult2 f (BenchResult name1 runs1) (BenchResult name2 runs2)+	| name1 == name2	= BenchResult name1 (f runs1 runs2)+	| otherwise		= error "liftBenchRunResult2: names don't match"+	++-- | Lift a function to the aspects of each `BenchRunResult`.+liftToAspectsOfBenchResult +	:: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)])+	-> BenchResult c1           -> BenchResult c2++liftToAspectsOfBenchResult +	= liftBenchRunResult . map . liftRunResultAspects+++-- | Lift a binary function to the aspects of each `BenchRunResult`.+liftToAspectsOfBenchResult2+	:: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)] -> [WithUnits (Aspect c3)])+	-> BenchResult c1           -> BenchResult c2          -> BenchResult c3++liftToAspectsOfBenchResult2+	= liftBenchRunResult2 . zipWith . liftRunResultAspects2++++-- BenchRunResult ---------------------------------------------------------------------------------+-- | Holds the result of running a benchmark once.+data BenchRunResult c+	= BenchRunResult+	{ -- | What iteration this run was.+	  --   Use 1 for the first ''real'' iteration derived by running a program.+	  --   Use 0 for ''fake'' iterations computed by statistics or comparisons.+	  benchRunResultIndex	:: Integer++	  -- | Aspects of the benchmark run.+	, benchRunResultAspects	:: [WithUnits (Aspect c)] }+++deriving instance +	(  Show (c Seconds), Show (c Bytes)) +	=> Show (BenchRunResult c)++deriving instance+	(  HasUnits (c Bytes) Bytes+	,  Read (c Bytes)+	,  HasUnits (c Seconds) Seconds+	,  Read (c Seconds))+	=> Read (BenchRunResult c)+++instance  ( Pretty (c Seconds), Pretty (c Bytes)) +	 => Pretty (BenchRunResult c) where+ ppr result+	| benchRunResultIndex result == 0+	=  (nest 2 $ vcat $ map ppr $ benchRunResultAspects result)++	| otherwise+	= ppr (benchRunResultIndex result) +	$$ (nest 2 $ vcat $ map ppr $ benchRunResultAspects result)+++-- Lifting ----------------------------------------------------------------------------------------+-- | Apply a function to the aspects of a `BenchRunResult`+appRunResultAspects :: ([WithUnits (Aspect c1)] -> b) -> BenchRunResult c1 -> b+appRunResultAspects f (BenchRunResult _ aspects) = f aspects+++-- | Lift a function to the aspects of a `BenchRunResult`+liftRunResultAspects+	:: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)])+	-> BenchRunResult c1        -> BenchRunResult c2+	+liftRunResultAspects f (BenchRunResult ix as)+	= BenchRunResult ix (f as)+++-- | Lift a binary function to the aspects of a `BenchRunResult`+liftRunResultAspects2+	:: ([WithUnits (Aspect c1)] -> [WithUnits (Aspect c2)] -> [WithUnits (Aspect c3)])+	-> BenchRunResult c1        -> BenchRunResult c2       -> BenchRunResult c3+	+liftRunResultAspects2 f (BenchRunResult ix1 as) (BenchRunResult ix2 bs)+	| ix1 == ix2		= BenchRunResult ix1 (f as bs)+	| otherwise		= error "liftRunResultAspects2: indices don't match"+
+ BuildBox/Benchmark/Benchmark.hs view
@@ -0,0 +1,25 @@++module BuildBox.Benchmark.Benchmark+	(Benchmark(..))+where+import BuildBox.Build+import BuildBox.Aspect++-- | Describes a benchmark that we can run.+data Benchmark+	= Benchmark+	{ -- | A unique name for the benchmark.+	  benchmarkName		:: String++	  -- | Setup command to run before the main benchmark.+	, benchmarkSetup	:: Build ()++	  -- | The benchmark command to run. +	  --   The time taken to run this part is automatically measured and added to the overall results.+	, benchmarkCommand	:: Build [WithUnits (Aspect Single)]++	  -- | Check \/ cleanup command to run after the main benchmark.+	, benchmarkCheck	:: Build [WithUnits (Aspect Single)]+	}++
− BuildBox/Benchmark/Compare.hs
@@ -1,50 +0,0 @@---- | Pretty printing comparisons of benchmark results.-module BuildBox.Benchmark.Compare-	( pprComparison-	, pprComparisons)-where-import BuildBox.Pretty-import BuildBox.Benchmark.Base-import BuildBox.Benchmark.Pretty-import BuildBox.Benchmark.TimeAspect-import Data.Maybe-import Data.List---- | Pretty print a comparison of all the aspects of these two benchmark results.---   The first result is the ``baseline'', while the second is the ``current'' one.---   The numbers from the current results are printed, with a percentage relative to the baseline.-pprComparison :: BenchResult -> BenchResult -> Doc-pprComparison baseline current- 	= vcat-	[ pprBenchResultAspectHeader-	, fromMaybe empty $ pprBenchResultAspect TimeAspectElapsed	 (Just baseline) current-	, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelElapsed (Just baseline) current-	, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelCpu	 (Just baseline) current-	, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelSys	 (Just baseline) current ]----- | Pretty print a comparison of all the aspects of these two benchmark results.---   The first result is the ``baseline'' while the second is the ``current'' one.---   All the numbers from the current results are printed, with a percentage relative to the---   baseline if there is one. If there is no baseline for a particular result then we still---   print the current one.-pprComparisons :: [BenchResult] -> [BenchResult] -> Doc-pprComparisons baselines currents- = let	comparison current-	 = let	mBaseline = find (\b -> benchResultName b == benchResultName current)-			  $ baselines--	   in	vcat-		[ fromMaybe empty $ pprBenchResultAspect TimeAspectElapsed	 mBaseline current-		, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelElapsed mBaseline current-		, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelCpu	 mBaseline current-		, fromMaybe empty $ pprBenchResultAspect TimeAspectKernelSys	 mBaseline current ]--   in	vcat-	[ nest 30 pprBenchResultAspectHeader-	, vcat	$ punctuate (text "\n")-		$ map (\c -> (text (benchResultName c) $$ (nest 30 $ comparison c)))-		$ currents ]--
− BuildBox/Benchmark/Pretty.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE PatternGuards #-}---- | Pretty printing benchmark results.-module BuildBox.Benchmark.Pretty-	( pprBenchResultAspectHeader-	, pprBenchResultAspect)-where-import BuildBox.Benchmark.Base-import BuildBox.Benchmark.TimeAspect-import BuildBox.Pretty---- | Header to use when pretty printing benchmark results.-pprBenchResultAspectHeader :: Doc-pprBenchResultAspectHeader -	=  vcat-	[ text "    aspect       min    ref%   avg    ref%   max    ref%   spread% "-	, text "    ---------    -----------   -----------   -----------   ------- " ]----- | Pretty print an aspect of a benchmark result.---   If the given aspect does not exist in the result then `Nothing`.-pprBenchResultAspect -	:: TimeAspect 			-- ^ Aspect of the result to print.-	-> Maybe BenchResult		-- ^ Optional prior result for comparison.-	-> BenchResult			-- ^ The result to print.-	-> Maybe Doc--pprBenchResultAspect aspect prior result- 	| Just (tmin,  tavg,  tmax)	<- takeMinAvgMaxOfBenchResult aspect result-	, spread			<- tmax - tmin-	, spreadPercent			<- (floor $ (spread / tavg) * 100) :: Integer-	, Just result'			<- prior-	, Just (tmin', tavg', tmax')	<- takeMinAvgMaxOfBenchResult aspect result'-	= Just	$   text "    "-		<>  padL 10 (ppr aspect)-		<+> (padR 13 $ pprFloatRef tmin tmin')-		<+> (padR 13 $ pprFloatRef tavg tavg')-		<+> (padR 13 $ pprFloatRef tmax tmax')-		<+> (padR 8  $ ppr spreadPercent)-- 	| Just (tmin, tavg, tmax)	<- takeMinAvgMaxOfBenchResult aspect result-	, spread			<- tmax - tmin-	, spreadPercent			<- (floor $ (spread / tavg) * 100) :: Integer-	= Just	$   text "    "-		<>  padL 10 (ppr aspect)-		<+> (padR 13 $ (pprFloatTime tmin <> text "     "))-		<+> (padR 13 $ (pprFloatTime tavg <> text "     "))-		<+> (padR 13 $ (pprFloatTime tmax <> text "     "))-		<+> (padR 8 $ ppr spreadPercent)-	-	| otherwise-	= Nothing	
− BuildBox/Benchmark/TimeAspect.hs
@@ -1,86 +0,0 @@-{-# LANGUAGE PatternGuards #-}---- | Dealing with aspects of timing results.-module BuildBox.Benchmark.TimeAspect-	( TimeAspect(..)-	, takeTimeAspectOfBenchRunResult-	, takeAvgTimeOfBenchResult-	, takeMinTimeOfBenchResult-	, takeMaxTimeOfBenchResult-	, takeMinAvgMaxOfBenchResult)-where-import BuildBox.Benchmark.Base-import BuildBox.Pretty-import Control.Monad----- | Aspects of a benchmark runtime we can talk about.-data TimeAspect-	= TimeAspectElapsed-	| TimeAspectKernelElapsed-	| TimeAspectKernelCpu-	| TimeAspectKernelSys-	deriving (Show, Read, Enum)----- | Get the pretty name of a TimeAspect.-instance Pretty TimeAspect where- ppr aspect-  = case aspect of-	TimeAspectElapsed		-> ppr "elapsed"-	TimeAspectKernelElapsed		-> ppr "k.elapsed"-	TimeAspectKernelCpu		-> ppr "k.cpu"-	TimeAspectKernelSys		-> ppr "k.system"----- | Get a particular aspect of a benchmark result.-takeTimeAspectOfBenchRunResult :: TimeAspect -> BenchRunResult -> Maybe Float-takeTimeAspectOfBenchRunResult aspect result- = case aspect of-	TimeAspectElapsed	-> Just $ benchRunResultElapsed result-	TimeAspectKernelElapsed	-> join $ liftM timingElapsed $ benchRunResultKernel result-	TimeAspectKernelCpu	-> join $ liftM timingCpu     $ benchRunResultKernel result-	TimeAspectKernelSys	-> join $ liftM timingSys     $ benchRunResultKernel result----- | Get the average runtime from a benchmark result.-takeAvgTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float-takeAvgTimeOfBenchResult aspect result- = let	mTimes	= sequence -		$ map (takeTimeAspectOfBenchRunResult aspect)-		$ benchResultRuns result-		-   in	liftM (\ts -> sum ts / (fromIntegral $ length ts)) mTimes-	---- | Get the minimum runtime from a benchmark result.-takeMinTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float-takeMinTimeOfBenchResult aspect result- = let	mTimes	= sequence-		$ map (takeTimeAspectOfBenchRunResult aspect)-		$ benchResultRuns result--   in	liftM (\ts -> minimum ts) mTimes----- | Get the maximum runtime from a benchmark result.-takeMaxTimeOfBenchResult :: TimeAspect -> BenchResult -> Maybe Float-takeMaxTimeOfBenchResult aspect result- = let	mTimes	= sequence-		$ map (takeTimeAspectOfBenchRunResult aspect)-		$ benchResultRuns result--   in	liftM (\ts -> maximum ts) mTimes----- | Get the min, avg, and max runtimes from this benchmark result.-takeMinAvgMaxOfBenchResult :: TimeAspect -> BenchResult -> Maybe (Float, Float, Float)-takeMinAvgMaxOfBenchResult aspect result-	| Just tmin	<- takeMinTimeOfBenchResult aspect result-	, Just tavg	<- takeAvgTimeOfBenchResult aspect result-	, Just tmax	<- takeMaxTimeOfBenchResult aspect result-	= Just (tmin, tavg, tmax)-	-	| otherwise-	= Nothing-
BuildBox/Build/Base.hs view
@@ -63,6 +63,8 @@   -- | Throw a needs error saying we needs the given file.+--   A catcher could then usefully create the file, or defer the compuation until it has been +--   created. needs :: FilePath -> Build () needs filePath  = do	isFile	<- io $ doesFileExist filePath
+ BuildBox/Data/Dividable.hs view
@@ -0,0 +1,13 @@++module BuildBox.Data.Dividable+	(Dividable(..))+where++class Dividable a where+	divide :: a -> a -> a+	+instance Dividable Integer where+	divide	= div++instance Dividable Float where+	divide	= (/)
BuildBox/FileFormat/BuildResults.hs view
@@ -7,6 +7,7 @@ import BuildBox.Benchmark import BuildBox.Command.Environment import BuildBox.Pretty+import BuildBox.Aspect import Data.List  @@ -15,7 +16,7 @@ 	= BuildResults 	{ buildResultTime		:: UTCTime 	, buildResultEnvironment	:: Environment-	, buildResultBench		:: [BenchResult] }+	, buildResultBench		:: [BenchResult Single] } 	deriving (Show, Read)  instance Pretty BuildResults where
BuildBox/IO/File.hs view
@@ -1,9 +1,10 @@-+{-# OPTIONS_HADDOCK hide #-} module BuildBox.IO.File 	(atomicWriteFile) where -+-- | Should atomically write a file by writing it to a tmp file then renaming it.+--   TODO: Does not yet work as advertised. atomicWriteFile :: FilePath -> String -> IO () atomicWriteFile filePath str  = do	writeFile filePath str
BuildBox/Pretty.hs view
@@ -1,19 +1,19 @@-{-# LANGUAGE TypeSynonymInstances, ScopedTypeVariables #-}+{-# LANGUAGE TypeSynonymInstances, ScopedTypeVariables, OverlappingInstances, IncoherentInstances #-}  -- | Pretty printing utils. module BuildBox.Pretty 	( module Text.PrettyPrint 	, Pretty(..)-	, pprPSecTime-	, pprFloatTime-	, pprFloatSR-	, pprFloatRef 	, padRc, padR 	, padLc, padL-	, blank)+	, blank+	, pprEngDouble+	, pprEngInteger) where import Text.PrettyPrint+import Text.Printf import Data.Time+import Control.Monad  -- Things that can be pretty printed class Pretty a where@@ -22,9 +22,6 @@ -- Basic instances instance Pretty Doc where 	ppr = id--instance Pretty String where-	ppr = text 	 instance Pretty Float where 	ppr = text . show@@ -38,52 +35,12 @@ instance Pretty UTCTime where 	ppr = text . show 	---- To handle type defaulting-ten12i :: Integer-ten12i = 10^(12 :: Integer)----- | Print a number of picoseconds as a time.-pprPSecTime :: Integer -> Doc-pprPSecTime psecs-  	=  text (show (psecs `quot` ten12i))-	<> text "." - 	<> (text $ (take 3 $ render $ padRc 12 '0' $ text $ show $ psecs `rem` ten12i))----- | Print a float number of seconds as a time.-pprFloatTime :: Float -> Doc-pprFloatTime stime-  = let (secs :: Integer, frac :: Float)	-			= properFraction stime--	msecs		= frac * 1000-    in	text (show secs) -	 <> text "."-	 <> (padRc 3 '0' $ text $ show $ ((round $ msecs) :: Integer) )----- | Pretty print a signed float, with a percentage change relative to a reference figure.---   Comes out like @0.235( +5)@ for a +5 percent swing.-pprFloatRef :: Float -> Float -> Doc-pprFloatRef stime stimeRef - = let	diff		= ((stime - stimeRef) / stimeRef )*100-   in	pprFloatTime stime-	 <> parens (padR 4 $ pprFloatSR diff)----- | Print a float number of seconds, rounding it and, prefixing with @+@ or @-@ appropriately.-pprFloatSR :: Float -> Doc-pprFloatSR p-	| p == 0-	= text "----"+instance Pretty a => Pretty [a] where+	ppr xx +		= lbrack <> (hcat $ punctuate (text ", ") (map ppr xx)) <> rbrack - 	| p > 0-	= text "+" <> (ppr $ (round p :: Integer))-	-	| otherwise-	= text "-" <> (ppr $ (round (negate p) :: Integer))+instance Pretty String where+	ppr = text   -- | Right justify a doc, padding with a given character.@@ -110,3 +67,53 @@ -- | Blank text. This is different different from `empty` because it comes out a a newline when used in a `vcat`. blank :: Doc blank = ppr ""+++-- | Like `pprEngDouble` but don't display fractional part when the value is < 1000.+--   Good for units where fractional values might not make sense (like bytes).+pprEngInteger :: String -> Integer -> Maybe Doc+pprEngInteger unit k+    | k < 0	 = liftM (text "-" <>) $ pprEngInteger unit (-k)+    | k > 1000	 = pprEngDouble unit (fromRational $ toRational k)+    | otherwise  = Just $ text $ printf "%5d%s " k unit+++-- | Pretty print an engineering value, to 4 significant figures.+--   Valid range is  10^(-24) (y\/yocto) to 10^(+24) (Y\/Yotta).+--   Out of range values yield Nothing.+--+--   examples:+--+--   @+--   liftM render $ pprEngDouble \"J\" 102400    ==>   Just \"1.024MJ\"+--   liftM render $ pprEngDouble \"s\" 0.0000123 ==>   Just \"12.30us\"+--   @+--+pprEngDouble :: String -> Double -> Maybe Doc+pprEngDouble unit k+    | k < 0      = liftM (text "-" <>) $ pprEngDouble unit (-k)+    | k >= 1e+27 = Nothing+    | k >= 1e+24 = Just $ (k*1e-24) `with` ("Y" ++ unit)+    | k >= 1e+21 = Just $ (k*1e-21) `with` ("Z" ++ unit)+    | k >= 1e+18 = Just $ (k*1e-18) `with` ("E" ++ unit)+    | k >= 1e+15 = Just $ (k*1e-15) `with` ("P" ++ unit)+    | k >= 1e+12 = Just $ (k*1e-12) `with` ("T" ++ unit)+    | k >= 1e+9  = Just $ (k*1e-9)  `with` ("G" ++ unit)+    | k >= 1e+6  = Just $ (k*1e-6)  `with` ("M" ++ unit)+    | k >= 1e+3  = Just $ (k*1e-3)  `with` ("k" ++ unit)+    | k >= 1     = Just $ k         `with` (unit ++ " ")+    | k >= 1e-3  = Just $ (k*1e+3)  `with` ("m" ++ unit)+    | k >= 1e-6  = Just $ (k*1e+6)  `with` ("u" ++ unit)+    | k >= 1e-9  = Just $ (k*1e+9)  `with` ("n" ++ unit)+    | k >= 1e-12 = Just $ (k*1e+12) `with` ("p" ++ unit)+    | k >= 1e-15 = Just $ (k*1e+15) `with` ("f" ++ unit)+    | k >= 1e-18 = Just $ (k*1e+18) `with` ("a" ++ unit)+    | k >= 1e-21 = Just $ (k*1e+21) `with` ("z" ++ unit)+    | k >= 1e-24 = Just $ (k*1e+24) `with` ("y" ++ unit)+    | k >= 1e-27 = Nothing+    | otherwise  = Just $ text $ printf "%5.0f%s " k unit+     where with (t :: Double) (u :: String)+		| t >= 1e3  = text $ printf "%.0f%s" t u+		| t >= 1e2  = text $ printf "%.1f%s" t u+		| t >= 1e1  = text $ printf "%.2f%s" t u+		| otherwise = text $ printf "%.3f%s" t u
+ BuildBox/Reports/BenchResult.hs view
@@ -0,0 +1,68 @@+{-# LANGUAGE PatternGuards #-}+{-# OPTIONS -fno-warn-missing-signatures #-}++module BuildBox.Reports.BenchResult+	(reportBenchResults)+where+import BuildBox.Pretty+import BuildBox.Aspect+import BuildBox.Benchmark.BenchResult+import Text.Printf+import Data.List++-- | Produce a human readable report of benchmark results.+--+--   If you only want the results within a fractional swing from the baseline+--   then pass something like @(Just 0.1)@ as the first parameter for a 10\% swing, +--   otherwise all results are printed.+--      +reportBenchResults :: Maybe Double -> [BenchResult StatsComparison] -> Doc++-- no swing specified, just report all the results.+reportBenchResults Nothing comparisons+	= vcat $ punctuate (text "\n") $ map ppr comparisons+	+reportBenchResults (Just swing) comparisons+ = let	resultLosers+	 = filter	(predBenchResult (predSwingStatsComparison (\x -> x > swing)))+			comparisons++	resultWinners_+	 = filter 	(predBenchResult (predSwingStatsComparison (\x -> x < (- swing)))) +			comparisons++	-- losers can't be winners+	resultWinners 	= deleteFirstsBy (\r1 r2 -> benchResultName r1 == benchResultName r2)+				resultWinners_  resultLosers++   in	vcat $	[ text "Total tests = " <> int (length comparisons)+		, blank] ++ [reportBenchResults' swing resultWinners resultLosers]++reportBenchResults' swing resultWinners resultLosers+ 	| []	<- resultWinners+	, []	<- resultLosers+	= text "ALL GOOD\n"+	+	| otherwise+	= let	docWinners +			| []	<- resultWinners+			= []+				+			| otherwise+			= [text "-- WINNERS (had a swing of < "+					<> text (printf "%+2.0f" (negate (swing * 100))) <> text "%)"+			$$ (vcat $ punctuate (text "\n") $ map ppr resultWinners) +			<> text "\n"]+				+		docLosers+			| []	<- resultLosers+			= []+				+			| otherwise+			= [text "-- LOSERS  (had a swing of > " +					<> text (printf "%+2.0f" (swing * 100)) <> text "%)"+			$$ (vcat $ punctuate (text "\n") $ map ppr resultLosers) +			<> text "\n"]+		+	  in	vcat $ docWinners ++ docLosers+		
buildbox.cabal view
@@ -1,5 +1,5 @@ Name:                buildbox-Version:             1.2.1.0+Version:             1.3.0.0 License:             BSD3 License-file:        LICENSE Author:              Ben Lippmeier@@ -10,10 +10,10 @@ Category:            Development, Testing Homepage:            http://code.haskell.org/~benl/code/buildbox-head Description:-        Includes functions for checking the host platform, running tests, capturing output,+        Includes utilities for checking the host platform, running tests, capturing output,         handling errors, comparing runtimes against a baseline, sending mail, running events to a schedule etc. -	Some of these functions are just wrappers around shell commands, so be careful about passing+	Some of the Command functions are just wrappers around shell commands, so be careful about passing 	them weirdly formed arguments. 	 Synopsis:@@ -39,11 +39,16 @@         -Wall    Exposed-modules:-        BuildBox.Data.Log-        BuildBox.Benchmark.Compare-        BuildBox.Benchmark.Pretty-        BuildBox.Benchmark.TimeAspect+        BuildBox+        BuildBox.Aspect.Detail+        BuildBox.Aspect.Stats+        BuildBox.Aspect.Units+        BuildBox.Aspect.Single+        BuildBox.Aspect.Comparison+        BuildBox.Aspect+        BuildBox.Benchmark.BenchResult         BuildBox.Benchmark+        BuildBox.Reports.BenchResult         BuildBox.Build.Testable         BuildBox.Build.BuildError         BuildBox.Build.BuildState@@ -56,15 +61,16 @@         BuildBox.Command.Sleep         BuildBox.Cron.Schedule         BuildBox.Cron+        BuildBox.Data.Log+        BuildBox.Data.Dividable         BuildBox.FileFormat.BuildResults         BuildBox.IO.Directory-        BuildBox.IO.File         BuildBox.Pretty         BuildBox.Time-        BuildBox    Other-modules:         BuildBox.Command.System.Internals-        BuildBox.Benchmark.Base         BuildBox.Build.Base-       +        BuildBox.Aspect.Aspect+        BuildBox.Benchmark.Benchmark+        BuildBox.IO.File