packages feed

criterion 1.4.1.0 → 1.5.0.0

raw patch · 13 files changed

+41/−1159 lines, 13 filesdep +criterion-measurementPVP ok

version bump matches the API change (PVP)

Dependencies added: criterion-measurement

API changes (from Hackage documentation)

- Criterion.Measurement: GCStatistics :: !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Int64 -> !Double -> !Double -> !Double -> !Double -> !Double -> !Double -> GCStatistics
- Criterion.Measurement: [gcStatsBytesAllocated] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsBytesCopied] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsCpuSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: [gcStatsCumulativeBytesUsed] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsCurrentBytesSlop] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsCurrentBytesUsed] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsGcCpuSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: [gcStatsGcWallSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: [gcStatsMaxBytesSlop] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsMaxBytesUsed] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsMutatorCpuSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: [gcStatsMutatorWallSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: [gcStatsNumByteUsageSamples] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsNumGcs] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsPeakMegabytesAllocated] :: GCStatistics -> !Int64
- Criterion.Measurement: [gcStatsWallSeconds] :: GCStatistics -> !Double
- Criterion.Measurement: applyGCStatistics :: Maybe GCStatistics -> Maybe GCStatistics -> Maybe GCStatistics -> Measured -> Measured
- Criterion.Measurement: data GCStatistics
- Criterion.Measurement: getCPUTime :: IO Double
- Criterion.Measurement: getCycles :: IO Word64
- Criterion.Measurement: getGCStatistics :: IO (Maybe GCStatistics)
- Criterion.Measurement: getTime :: IO Double
- Criterion.Measurement: initializeTime :: IO ()
- Criterion.Measurement: instance Data.Data.Data Criterion.Measurement.GCStatistics
- Criterion.Measurement: instance GHC.Classes.Eq Criterion.Measurement.GCStatistics
- Criterion.Measurement: instance GHC.Generics.Generic Criterion.Measurement.GCStatistics
- Criterion.Measurement: instance GHC.Read.Read Criterion.Measurement.GCStatistics
- Criterion.Measurement: instance GHC.Show.Show Criterion.Measurement.GCStatistics
- Criterion.Measurement: measure :: Benchmarkable -> Int64 -> IO (Measured, Double)
- Criterion.Measurement: measured :: Measured
- Criterion.Measurement: runBenchmark :: Benchmarkable -> Double -> IO (Vector Measured, Double)
- Criterion.Measurement: runBenchmarkable :: Benchmarkable -> Int64 -> (a -> a -> a) -> (Int64 -> IO () -> IO a) -> IO a
- Criterion.Measurement: runBenchmarkable_ :: Benchmarkable -> Int64 -> IO ()
- Criterion.Measurement: secs :: Double -> String
- Criterion.Measurement: threshold :: Double
- Criterion.Types: Benchmarkable :: (Int64 -> IO a) -> (Int64 -> a -> IO ()) -> (a -> Int64 -> IO ()) -> Bool -> Benchmarkable
- Criterion.Types: [allocEnv] :: Benchmarkable -> Int64 -> IO a
- Criterion.Types: [cleanEnv] :: Benchmarkable -> Int64 -> a -> IO ()
- Criterion.Types: [perRun] :: Benchmarkable -> Bool
- Criterion.Types: [runRepeatedly] :: Benchmarkable -> a -> Int64 -> IO ()
- Criterion.Types: instance Control.DeepSeq.NFData Criterion.Types.Measured
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.DataRecord
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.KDE
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.Measured
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.OutlierEffect
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.OutlierVariance
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.Outliers
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.Regression
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.Report
- Criterion.Types: instance Data.Aeson.Types.Class.FromJSON Criterion.Types.SampleAnalysis
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.DataRecord
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.KDE
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.Measured
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.OutlierEffect
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.OutlierVariance
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.Outliers
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.Regression
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.Report
- Criterion.Types: instance Data.Aeson.Types.Class.ToJSON Criterion.Types.SampleAnalysis
- Criterion.Types: instance Data.Binary.Class.Binary Criterion.Types.Measured
- Criterion.Types: instance Data.Data.Data Criterion.Types.Measured
- Criterion.Types: instance Data.Semigroup.Semigroup Criterion.Types.Outliers
- Criterion.Types: instance GHC.Classes.Eq Criterion.Types.Measured
- Criterion.Types: instance GHC.Generics.Generic Criterion.Types.Measured
- Criterion.Types: instance GHC.Read.Read Criterion.Types.Measured
- Criterion.Types: instance GHC.Show.Show Criterion.Types.Benchmark
- Criterion.Types: instance GHC.Show.Show Criterion.Types.Measured
+ Criterion.Types: [Benchmarkable] :: Benchmarkable
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.DataRecord
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.KDE
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.OutlierEffect
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.OutlierVariance
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.Outliers
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.Regression
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.Report
+ Criterion.Types: instance Data.Aeson.Types.FromJSON.FromJSON Criterion.Types.SampleAnalysis
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.DataRecord
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.KDE
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.OutlierEffect
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.OutlierVariance
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.Outliers
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.Regression
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.Report
+ Criterion.Types: instance Data.Aeson.Types.ToJSON.ToJSON Criterion.Types.SampleAnalysis
+ Criterion.Types: instance GHC.Base.Semigroup Criterion.Types.Outliers
- Criterion: env :: NFData env => IO env -> (env -> Benchmark) -> Benchmark
+ Criterion: env :: NFData env => IO env -> env -> Benchmark -> Benchmark
- Criterion: envWithCleanup :: NFData env => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark
+ Criterion: envWithCleanup :: NFData env => IO env -> env -> IO a -> env -> Benchmark -> Benchmark
- Criterion: nf :: NFData b => (a -> b) -> a -> Benchmarkable
+ Criterion: nf :: NFData b => a -> b -> a -> Benchmarkable
- Criterion: perBatchEnv :: (NFData env, NFData b) => (Int64 -> IO env) -> (env -> IO b) -> Benchmarkable
+ Criterion: perBatchEnv :: (NFData env, NFData b) => Int64 -> IO env -> env -> IO b -> Benchmarkable
- Criterion: perBatchEnvWithCleanup :: (NFData env, NFData b) => (Int64 -> IO env) -> (Int64 -> env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion: perBatchEnvWithCleanup :: (NFData env, NFData b) => Int64 -> IO env -> Int64 -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion: perRunEnv :: (NFData env, NFData b) => IO env -> (env -> IO b) -> Benchmarkable
+ Criterion: perRunEnv :: (NFData env, NFData b) => IO env -> env -> IO b -> Benchmarkable
- Criterion: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> (env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion: toBenchmarkable :: (Int64 -> IO ()) -> Benchmarkable
+ Criterion: toBenchmarkable :: Int64 -> IO () -> Benchmarkable
- Criterion: whnf :: (a -> b) -> a -> Benchmarkable
+ Criterion: whnf :: () => a -> b -> a -> Benchmarkable
- Criterion: whnfIO :: IO a -> Benchmarkable
+ Criterion: whnfIO :: () => IO a -> Benchmarkable
- Criterion.Main: env :: NFData env => IO env -> (env -> Benchmark) -> Benchmark
+ Criterion.Main: env :: NFData env => IO env -> env -> Benchmark -> Benchmark
- Criterion.Main: envWithCleanup :: NFData env => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark
+ Criterion.Main: envWithCleanup :: NFData env => IO env -> env -> IO a -> env -> Benchmark -> Benchmark
- Criterion.Main: nf :: NFData b => (a -> b) -> a -> Benchmarkable
+ Criterion.Main: nf :: NFData b => a -> b -> a -> Benchmarkable
- Criterion.Main: perBatchEnv :: (NFData env, NFData b) => (Int64 -> IO env) -> (env -> IO b) -> Benchmarkable
+ Criterion.Main: perBatchEnv :: (NFData env, NFData b) => Int64 -> IO env -> env -> IO b -> Benchmarkable
- Criterion.Main: perBatchEnvWithCleanup :: (NFData env, NFData b) => (Int64 -> IO env) -> (Int64 -> env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion.Main: perBatchEnvWithCleanup :: (NFData env, NFData b) => Int64 -> IO env -> Int64 -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion.Main: perRunEnv :: (NFData env, NFData b) => IO env -> (env -> IO b) -> Benchmarkable
+ Criterion.Main: perRunEnv :: (NFData env, NFData b) => IO env -> env -> IO b -> Benchmarkable
- Criterion.Main: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> (env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion.Main: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion.Main: toBenchmarkable :: (Int64 -> IO ()) -> Benchmarkable
+ Criterion.Main: toBenchmarkable :: Int64 -> IO () -> Benchmarkable
- Criterion.Main: whnf :: (a -> b) -> a -> Benchmarkable
+ Criterion.Main: whnf :: () => a -> b -> a -> Benchmarkable
- Criterion.Main: whnfIO :: IO a -> Benchmarkable
+ Criterion.Main: whnfIO :: () => IO a -> Benchmarkable
- Criterion.Types: [BenchGroup] :: String -> [Benchmark] -> Benchmark
+ Criterion.Types: [BenchGroup] :: Benchmark
- Criterion.Types: [Benchmark] :: String -> Benchmarkable -> Benchmark
+ Criterion.Types: [Benchmark] :: Benchmark
- Criterion.Types: [Environment] :: NFData env => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark
+ Criterion.Types: [Environment] :: Benchmark
- Criterion.Types: env :: NFData env => IO env -> (env -> Benchmark) -> Benchmark
+ Criterion.Types: env :: NFData env => IO env -> env -> Benchmark -> Benchmark
- Criterion.Types: envWithCleanup :: NFData env => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark
+ Criterion.Types: envWithCleanup :: NFData env => IO env -> env -> IO a -> env -> Benchmark -> Benchmark
- Criterion.Types: measure :: (Unbox a) => (Measured -> a) -> Vector Measured -> Vector a
+ Criterion.Types: measure :: Unbox a => Measured -> a -> Vector Measured -> Vector a
- Criterion.Types: nf :: NFData b => (a -> b) -> a -> Benchmarkable
+ Criterion.Types: nf :: NFData b => a -> b -> a -> Benchmarkable
- Criterion.Types: perBatchEnv :: (NFData env, NFData b) => (Int64 -> IO env) -> (env -> IO b) -> Benchmarkable
+ Criterion.Types: perBatchEnv :: (NFData env, NFData b) => Int64 -> IO env -> env -> IO b -> Benchmarkable
- Criterion.Types: perBatchEnvWithCleanup :: (NFData env, NFData b) => (Int64 -> IO env) -> (Int64 -> env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion.Types: perBatchEnvWithCleanup :: (NFData env, NFData b) => Int64 -> IO env -> Int64 -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion.Types: perRunEnv :: (NFData env, NFData b) => IO env -> (env -> IO b) -> Benchmarkable
+ Criterion.Types: perRunEnv :: (NFData env, NFData b) => IO env -> env -> IO b -> Benchmarkable
- Criterion.Types: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> (env -> IO ()) -> (env -> IO b) -> Benchmarkable
+ Criterion.Types: perRunEnvWithCleanup :: (NFData env, NFData b) => IO env -> env -> IO () -> env -> IO b -> Benchmarkable
- Criterion.Types: toBenchmarkable :: (Int64 -> IO ()) -> Benchmarkable
+ Criterion.Types: toBenchmarkable :: Int64 -> IO () -> Benchmarkable
- Criterion.Types: whnf :: (a -> b) -> a -> Benchmarkable
+ Criterion.Types: whnf :: () => a -> b -> a -> Benchmarkable
- Criterion.Types: whnfIO :: IO a -> Benchmarkable
+ Criterion.Types: whnfIO :: () => IO a -> Benchmarkable

Files

Criterion/Internal.hs view
@@ -36,7 +36,7 @@ import Criterion.Monad (Criterion) import Criterion.Report (report) import Criterion.Types hiding (measure)-import Criterion.Types.Internal (fakeEnvironment)+import Criterion.Measurement.Types.Internal (fakeEnvironment) import qualified Data.Map as Map import qualified Data.Vector as V import Statistics.Types (Estimate(..),ConfInt(..),confidenceInterval,cl95,confidenceLevel)
Criterion/Main.hs view
@@ -11,6 +11,10 @@ -- -- Wrappers for compiling and running benchmarks quickly and easily. -- See 'defaultMain' below for an example.+--+-- All of the 'IO'-returning functions in this module initialize the timer+-- before measuring time (refer to the documentation for 'initializeTime'+-- for more details).  module Criterion.Main     (
− Criterion/Measurement.hs
@@ -1,399 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE Trustworthy #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE BangPatterns, CPP, ForeignFunctionInterface,-    ScopedTypeVariables #-}---- |--- Module      : Criterion.Measurement--- Copyright   : (c) 2009-2014 Bryan O'Sullivan------ License     : BSD-style--- Maintainer  : bos@serpentine.com--- Stability   : experimental--- Portability : GHC------ Benchmark measurement code.--module Criterion.Measurement-    (-      initializeTime-    , getTime-    , getCPUTime-    , getCycles-    , getGCStatistics-    , GCStatistics(..)-    , secs-    , measure-    , runBenchmark-    , runBenchmarkable-    , runBenchmarkable_-    , measured-    , applyGCStatistics-    , threshold-    ) where--import Criterion.Types (Benchmarkable(..), Measured(..))-import Control.DeepSeq (NFData(rnf))-import Control.Exception (finally,evaluate)-import Data.Data (Data, Typeable)-import Data.Int (Int64)-import Data.List (unfoldr)-import Data.Word (Word64)-import GHC.Generics (Generic)-#if MIN_VERSION_base(4,10,0)-import GHC.Stats (RTSStats(..), GCDetails(..))-#else-import GHC.Stats (GCStats(..))-#endif-import Prelude ()-import Prelude.Compat-#if MIN_VERSION_base(4,7,0)-import System.Mem (performGC, performMinorGC)-# else-import System.Mem (performGC)-#endif-import Text.Printf (printf)-import qualified Control.Exception as Exc-import qualified Data.Vector as V-import qualified GHC.Stats as Stats--#if !(MIN_VERSION_base(4,7,0))-foreign import ccall "performGC" performMinorGC :: IO ()-#endif---- | Statistics about memory usage and the garbage collector. Apart from--- 'gcStatsCurrentBytesUsed' and 'gcStatsCurrentBytesSlop' all are cumulative values since--- the program started.------ 'GCStatistics' is cargo-culted from the @GCStats@ data type that "GHC.Stats"--- used to export. Since @GCStats@ was removed in GHC 8.4, @criterion@ uses--- 'GCStatistics' to provide a backwards-compatible view of GC statistics.-data GCStatistics = GCStatistics-    { -- | Total number of bytes allocated-    gcStatsBytesAllocated :: !Int64-    -- | Number of garbage collections performed (any generation, major and-    -- minor)-    , gcStatsNumGcs :: !Int64-    -- | Maximum number of live bytes seen so far-    , gcStatsMaxBytesUsed :: !Int64-    -- | Number of byte usage samples taken, or equivalently-    -- the number of major GCs performed.-    , gcStatsNumByteUsageSamples :: !Int64-    -- | Sum of all byte usage samples, can be used with-    -- 'gcStatsNumByteUsageSamples' to calculate averages with-    -- arbitrary weighting (if you are sampling this record multiple-    -- times).-    , gcStatsCumulativeBytesUsed :: !Int64-    -- | Number of bytes copied during GC-    , gcStatsBytesCopied :: !Int64-    -- | Number of live bytes at the end of the last major GC-    , gcStatsCurrentBytesUsed :: !Int64-    -- | Current number of bytes lost to slop-    , gcStatsCurrentBytesSlop :: !Int64-    -- | Maximum number of bytes lost to slop at any one time so far-    , gcStatsMaxBytesSlop :: !Int64-    -- | Maximum number of megabytes allocated-    , gcStatsPeakMegabytesAllocated :: !Int64-    -- | CPU time spent running mutator threads.  This does not include-    -- any profiling overhead or initialization.-    , gcStatsMutatorCpuSeconds :: !Double--    -- | Wall clock time spent running mutator threads.  This does not-    -- include initialization.-    , gcStatsMutatorWallSeconds :: !Double-    -- | CPU time spent running GC-    , gcStatsGcCpuSeconds :: !Double-    -- | Wall clock time spent running GC-    , gcStatsGcWallSeconds :: !Double-    -- | Total CPU time elapsed since program start-    , gcStatsCpuSeconds :: !Double-    -- | Total wall clock time elapsed since start-    , gcStatsWallSeconds :: !Double-    } deriving (Eq, Read, Show, Typeable, Data, Generic)---- | Try to get GC statistics, bearing in mind that the GHC runtime--- will throw an exception if statistics collection was not enabled--- using \"@+RTS -T@\".--- --- If you need guaranteed up-to-date stats, call 'performGC' first.-getGCStatistics :: IO (Maybe GCStatistics)-#if MIN_VERSION_base(4,10,0)--- Use RTSStats/GCDetails to gather GC stats-getGCStatistics = do-  stats <- Stats.getRTSStats-  let gcdetails :: Stats.GCDetails-      gcdetails = gc stats--      nsToSecs :: Int64 -> Double-      nsToSecs ns = fromIntegral ns * 1.0E-9--  return $ Just GCStatistics {-      gcStatsBytesAllocated         = fromIntegral $ allocated_bytes stats-    , gcStatsNumGcs                 = fromIntegral $ gcs stats-    , gcStatsMaxBytesUsed           = fromIntegral $ max_live_bytes stats-    , gcStatsNumByteUsageSamples    = fromIntegral $ major_gcs stats-    , gcStatsCumulativeBytesUsed    = fromIntegral $ cumulative_live_bytes stats-    , gcStatsBytesCopied            = fromIntegral $ copied_bytes stats-    , gcStatsCurrentBytesUsed       = fromIntegral $ gcdetails_live_bytes gcdetails-    , gcStatsCurrentBytesSlop       = fromIntegral $ gcdetails_slop_bytes gcdetails-    , gcStatsMaxBytesSlop           = fromIntegral $ max_slop_bytes stats-    , gcStatsPeakMegabytesAllocated = fromIntegral (max_mem_in_use_bytes stats) `quot` (1024*1024)-    , gcStatsMutatorCpuSeconds      = nsToSecs $ mutator_cpu_ns stats-    , gcStatsMutatorWallSeconds     = nsToSecs $ mutator_elapsed_ns stats-    , gcStatsGcCpuSeconds           = nsToSecs $ gc_cpu_ns stats-    , gcStatsGcWallSeconds          = nsToSecs $ gc_elapsed_ns stats-    , gcStatsCpuSeconds             = nsToSecs $ cpu_ns stats-    , gcStatsWallSeconds            = nsToSecs $ elapsed_ns stats-    }- `Exc.catch`-  \(_::Exc.SomeException) -> return Nothing-#else--- Use the old GCStats type to gather GC stats-getGCStatistics = do-  stats <- Stats.getGCStats-  return $ Just GCStatistics {-      gcStatsBytesAllocated         = bytesAllocated stats-    , gcStatsNumGcs                 = numGcs stats-    , gcStatsMaxBytesUsed           = maxBytesUsed stats-    , gcStatsNumByteUsageSamples    = numByteUsageSamples stats-    , gcStatsCumulativeBytesUsed    = cumulativeBytesUsed stats-    , gcStatsBytesCopied            = bytesCopied stats-    , gcStatsCurrentBytesUsed       = currentBytesUsed stats-    , gcStatsCurrentBytesSlop       = currentBytesSlop stats-    , gcStatsMaxBytesSlop           = maxBytesSlop stats-    , gcStatsPeakMegabytesAllocated = peakMegabytesAllocated stats-    , gcStatsMutatorCpuSeconds      = mutatorCpuSeconds stats-    , gcStatsMutatorWallSeconds     = mutatorWallSeconds stats-    , gcStatsGcCpuSeconds           = gcCpuSeconds stats-    , gcStatsGcWallSeconds          = gcWallSeconds stats-    , gcStatsCpuSeconds             = cpuSeconds stats-    , gcStatsWallSeconds            = wallSeconds stats-    }- `Exc.catch`-  \(_::Exc.SomeException) -> return Nothing-#endif---- | Measure the execution of a benchmark a given number of times.-measure :: Benchmarkable        -- ^ Operation to benchmark.-        -> Int64                -- ^ Number of iterations.-        -> IO (Measured, Double)-measure bm iters = runBenchmarkable bm iters addResults $ \ !n act -> do-  -- Ensure the stats from getGCStatistics are up-to-date-  -- by garbage collecting. performMinorGC does /not/ update all stats, but-  -- it does update the ones we need (see applyGCStatistics for details.-  ---  -- We use performMinorGC instead of performGC to avoid the cost of copying-  -- the live data in the heap potentially hundreds of times in a-  -- single benchmark.-  performMinorGC-  startStats <- getGCStatistics-  startTime <- getTime-  startCpuTime <- getCPUTime-  startCycles <- getCycles-  act-  endTime <- getTime-  endCpuTime <- getCPUTime-  endCycles <- getCycles-  -- From these we can derive GC-related deltas.-  endStatsPreGC <- getGCStatistics-  performMinorGC-  -- From these we can derive all other deltas, and performGC guarantees they-  -- are up-to-date.-  endStatsPostGC <- getGCStatistics-  let !m = applyGCStatistics endStatsPostGC endStatsPreGC startStats $ measured {-             measTime    = max 0 (endTime - startTime)-           , measCpuTime = max 0 (endCpuTime - startCpuTime)-           , measCycles  = max 0 (fromIntegral (endCycles - startCycles))-           , measIters   = n-           }-  return (m, endTime)-  where-    -- When combining runs, the Measured value is accumulated over many runs,-    -- but the Double value is the most recent absolute measurement of time.-    addResults :: (Measured, Double) -> (Measured, Double) -> (Measured, Double)-    addResults (!m1, _) (!m2, !d2) = (m3, d2)-      where-        add f = f m1 + f m2--        m3 = Measured-            { measTime               = add measTime-            , measCpuTime            = add measCpuTime-            , measCycles             = add measCycles-            , measIters              = add measIters--            , measAllocated          = add measAllocated-            , measNumGcs             = add measNumGcs-            , measBytesCopied        = add measBytesCopied-            , measMutatorWallSeconds = add measMutatorWallSeconds-            , measMutatorCpuSeconds  = add measMutatorCpuSeconds-            , measGcWallSeconds      = add measGcWallSeconds-            , measGcCpuSeconds       = add measGcCpuSeconds-            }-{-# INLINE measure #-}---- | The amount of time a benchmark must run for in order for us to--- have some trust in the raw measurement.------ We set this threshold so that we can generate enough data to later--- perform meaningful statistical analyses.------ The threshold is 30 milliseconds. One use of 'runBenchmark' must--- accumulate more than 300 milliseconds of total measurements above--- this threshold before it will finish.-threshold :: Double-threshold = 0.03-{-# INLINE threshold #-}--runBenchmarkable :: Benchmarkable -> Int64 -> (a -> a -> a) -> (Int64 -> IO () -> IO a) -> IO a-runBenchmarkable Benchmarkable{..} i comb f-    | perRun = work >>= go (i - 1)-    | otherwise = work-  where-    go 0 result = return result-    go !n !result = work >>= go (n - 1) . comb result--    count | perRun = 1-          | otherwise = i--    work = do-        env <- allocEnv count-        let clean = cleanEnv count env-            run = runRepeatedly env count--        clean `seq` run `seq` evaluate $ rnf env--        f count run `finally` clean-    {-# INLINE work #-}-{-# INLINE runBenchmarkable #-}--runBenchmarkable_ :: Benchmarkable -> Int64 -> IO ()-runBenchmarkable_ bm i = runBenchmarkable bm i (\() () -> ()) (const id)-{-# INLINE runBenchmarkable_ #-}---- | Run a single benchmark, and return measurements collected while--- executing it, along with the amount of time the measurement process--- took.-runBenchmark :: Benchmarkable-             -> Double-             -- ^ Lower bound on how long the benchmarking process-             -- should take.  In practice, this time limit may be-             -- exceeded in order to generate enough data to perform-             -- meaningful statistical analyses.-             -> IO (V.Vector Measured, Double)-runBenchmark bm timeLimit = do-  runBenchmarkable_ bm 1-  start <- performGC >> getTime-  let loop [] !_ !_ _ = error "unpossible!"-      loop (iters:niters) prev count acc = do-        (m, endTime) <- measure bm iters-        let overThresh = max 0 (measTime m - threshold) + prev-        -- We try to honour the time limit, but we also have more-        -- important constraints:-        ---        -- We must generate enough data that bootstrapping won't-        -- simply crash.-        ---        -- We need to generate enough measurements that have long-        -- spans of execution to outweigh the (rather high) cost of-        -- measurement.-        if endTime - start >= timeLimit &&-           overThresh > threshold * 10 &&-           count >= (4 :: Int)-          then do-            let !v = V.reverse (V.fromList acc)-            return (v, endTime - start)-          else loop niters overThresh (count+1) (m:acc)-  loop (squish (unfoldr series 1)) 0 0 []---- Our series starts its growth very slowly when we begin at 1, so we--- eliminate repeated values.-squish :: (Eq a) => [a] -> [a]-squish ys = foldr go [] ys-  where go x xs = x : dropWhile (==x) xs--series :: Double -> Maybe (Int64, Double)-series k = Just (truncate l, l)-  where l = k * 1.05---- | An empty structure.-measured :: Measured-measured = Measured {-      measTime               = 0-    , measCpuTime            = 0-    , measCycles             = 0-    , measIters              = 0--    , measAllocated          = minBound-    , measNumGcs             = minBound-    , measBytesCopied        = minBound-    , measMutatorWallSeconds = bad-    , measMutatorCpuSeconds  = bad-    , measGcWallSeconds      = bad-    , measGcCpuSeconds       = bad-    } where bad = -1/0---- | Apply the difference between two sets of GC statistics to a--- measurement.-applyGCStatistics :: Maybe GCStatistics-                  -- ^ Statistics gathered at the __end__ of a run, post-GC.-                  -> Maybe GCStatistics-                  -- ^ Statistics gathered at the __end__ of a run, pre-GC.-                  -> Maybe GCStatistics-                  -- ^ Statistics gathered at the __beginning__ of a run.-                  -> Measured-                  -- ^ Value to \"modify\".-                  -> Measured-applyGCStatistics (Just endPostGC) (Just endPreGC) (Just start) m = m {-    -- The choice of endPostGC or endPreGC is important.-    -- For bytes allocated/copied, and mutator statistics, we use-    -- endPostGC, because the intermediate performGC ensures they're up-to-date.-    -- The others (num GCs and GC cpu/wall seconds) must be diffed against-    -- endPreGC so that the extra performGC does not taint them.-    measAllocated          = diff endPostGC gcStatsBytesAllocated-  , measNumGcs             = diff endPreGC  gcStatsNumGcs-  , measBytesCopied        = diff endPostGC gcStatsBytesCopied-  , measMutatorWallSeconds = diff endPostGC gcStatsMutatorWallSeconds-  , measMutatorCpuSeconds  = diff endPostGC gcStatsMutatorCpuSeconds-  , measGcWallSeconds      = diff endPreGC  gcStatsGcWallSeconds-  , measGcCpuSeconds       = diff endPreGC  gcStatsGcCpuSeconds-  } where diff a f = f a - f start-applyGCStatistics _ _ _ m = m---- | Convert a number of seconds to a string.  The string will consist--- of four decimal places, followed by a short description of the time--- units.-secs :: Double -> String-secs k-    | k < 0      = '-' : secs (-k)-    | k >= 1     = k        `with` "s"-    | k >= 1e-3  = (k*1e3)  `with` "ms"-    | k >= 1e-6  = (k*1e6)  `with` "μs"-    | k >= 1e-9  = (k*1e9)  `with` "ns"-    | k >= 1e-12 = (k*1e12) `with` "ps"-    | k >= 1e-15 = (k*1e15) `with` "fs"-    | k >= 1e-18 = (k*1e18) `with` "as"-    | otherwise  = printf "%g s" k-     where with (t :: Double) (u :: String)-               | t >= 1e9  = printf "%.4g %s" t u-               | t >= 1e3  = printf "%.0f %s" t u-               | t >= 1e2  = printf "%.1f %s" t u-               | t >= 1e1  = printf "%.2f %s" t u-               | otherwise = printf "%.3f %s" t u---- | Set up time measurement.-foreign import ccall unsafe "criterion_inittime" initializeTime :: IO ()---- | Read the CPU cycle counter.-foreign import ccall unsafe "criterion_rdtsc" getCycles :: IO Word64---- | Return the current wallclock time, in seconds since some--- arbitrary time.------ You /must/ call 'initializeTime' once before calling this function!-foreign import ccall unsafe "criterion_gettime" getTime :: IO Double---- | Return the amount of elapsed CPU time, combining user and kernel--- (system) time into a single measure.-foreign import ccall unsafe "criterion_getcputime" getCPUTime :: IO Double
Criterion/Types.hs view
@@ -73,12 +73,12 @@     ) where  import Control.DeepSeq (NFData(rnf))-import Criterion.Types.Internal (fakeEnvironment, nf', whnf')+import Criterion.Measurement.Types import Data.Aeson (FromJSON(..), ToJSON(..)) import Data.Binary (Binary(..), putWord8, getWord8) import Data.Data (Data, Typeable) import Data.Int (Int64)-import Data.Map (Map, fromList)+import Data.Map (Map) import GHC.Generics (Generic) import Prelude () import Prelude.Compat@@ -124,490 +124,6 @@       -- ^ Template file to use if writing a report.     } deriving (Eq, Read, Show, Typeable, Data, Generic) --- | A pure function or impure action that can be benchmarked. The--- 'Int64' parameter indicates the number of times to run the given--- function or action.-data Benchmarkable = forall a . NFData a =>-    Benchmarkable-      { allocEnv :: Int64 -> IO a-      , cleanEnv :: Int64 -> a -> IO ()-      , runRepeatedly :: a -> Int64 -> IO ()-      , perRun :: Bool-      }--noop :: Monad m => a -> m ()-noop = const $ return ()-{-# INLINE noop #-}---- | Construct a 'Benchmarkable' value from an impure action, where the 'Int64'--- parameter indicates the number of times to run the action.-toBenchmarkable :: (Int64 -> IO ()) -> Benchmarkable-toBenchmarkable f = Benchmarkable noop (const noop) (const f) False-{-# INLINE toBenchmarkable #-}---- | A collection of measurements made while benchmarking.------ Measurements related to garbage collection are tagged with __GC__.--- They will only be available if a benchmark is run with @\"+RTS--- -T\"@.------ __Packed storage.__ When GC statistics cannot be collected, GC--- values will be set to huge negative values.  If a field is labeled--- with \"__GC__\" below, use 'fromInt' and 'fromDouble' to safely--- convert to \"real\" values.-data Measured = Measured {-      measTime               :: !Double-      -- ^ Total wall-clock time elapsed, in seconds.-    , measCpuTime            :: !Double-      -- ^ Total CPU time elapsed, in seconds.  Includes both user and-      -- kernel (system) time.-    , measCycles             :: !Int64-      -- ^ Cycles, in unspecified units that may be CPU cycles.  (On-      -- i386 and x86_64, this is measured using the @rdtsc@-      -- instruction.)-    , measIters              :: !Int64-      -- ^ Number of loop iterations measured.--    , measAllocated          :: !Int64-      -- ^ __(GC)__ Number of bytes allocated.  Access using 'fromInt'.-    , measNumGcs             :: !Int64-      -- ^ __(GC)__ Number of garbage collections performed.  Access-      -- using 'fromInt'.-    , measBytesCopied        :: !Int64-      -- ^ __(GC)__ Number of bytes copied during garbage collection.-      -- Access using 'fromInt'.-    , measMutatorWallSeconds :: !Double-      -- ^ __(GC)__ Wall-clock time spent doing real work-      -- (\"mutation\"), as distinct from garbage collection.  Access-      -- using 'fromDouble'.-    , measMutatorCpuSeconds  :: !Double-      -- ^ __(GC)__ CPU time spent doing real work (\"mutation\"), as-      -- distinct from garbage collection.  Access using 'fromDouble'.-    , measGcWallSeconds      :: !Double-      -- ^ __(GC)__ Wall-clock time spent doing garbage collection.-      -- Access using 'fromDouble'.-    , measGcCpuSeconds       :: !Double-      -- ^ __(GC)__ CPU time spent doing garbage collection.  Access-      -- using 'fromDouble'.-    } deriving (Eq, Read, Show, Typeable, Data, Generic)--instance FromJSON Measured where-    parseJSON v = do-      (a,b,c,d,e,f,g,h,i,j,k) <- parseJSON v-      -- The first four fields are not subject to the encoding policy:-      return $ Measured a b c d-                       (int e) (int f) (int g)-                       (db h) (db i) (db j) (db k)-      where int = toInt; db = toDouble---- Here we treat the numeric fields as `Maybe Int64` and `Maybe Double`--- and we use a specific policy for deciding when they should be Nothing,--- which becomes null in JSON.-instance ToJSON Measured where-    toJSON Measured{..} = toJSON-      (measTime, measCpuTime, measCycles, measIters,-       i measAllocated, i measNumGcs, i measBytesCopied,-       d measMutatorWallSeconds, d measMutatorCpuSeconds,-       d measGcWallSeconds, d measGcCpuSeconds)-      where i = fromInt; d = fromDouble--instance NFData Measured where-    rnf Measured{} = ()---- THIS MUST REFLECT THE ORDER OF FIELDS IN THE DATA TYPE.------ The ordering is used by Javascript code to pick out the correct--- index into the vector that represents a Measured value in that--- world.-measureAccessors_ :: [(String, (Measured -> Maybe Double, String))]-measureAccessors_ = [-    ("time",               (Just . measTime,-                            "wall-clock time"))-  , ("cpuTime",            (Just . measCpuTime,-                            "CPU time"))-  , ("cycles",             (Just . fromIntegral . measCycles,-                            "CPU cycles"))-  , ("iters",              (Just . fromIntegral . measIters,-                            "loop iterations"))-  , ("allocated",          (fmap fromIntegral . fromInt . measAllocated,-                            "(+RTS -T) bytes allocated"))-  , ("numGcs",             (fmap fromIntegral . fromInt . measNumGcs,-                            "(+RTS -T) number of garbage collections"))-  , ("bytesCopied",        (fmap fromIntegral . fromInt . measBytesCopied,-                            "(+RTS -T) number of bytes copied during GC"))-  , ("mutatorWallSeconds", (fromDouble . measMutatorWallSeconds,-                            "(+RTS -T) wall-clock time for mutator threads"))-  , ("mutatorCpuSeconds",  (fromDouble . measMutatorCpuSeconds,-                            "(+RTS -T) CPU time spent running mutator threads"))-  , ("gcWallSeconds",      (fromDouble . measGcWallSeconds,-                            "(+RTS -T) wall-clock time spent doing GC"))-  , ("gcCpuSeconds",       (fromDouble . measGcCpuSeconds,-                            "(+RTS -T) CPU time spent doing GC"))-  ]---- | Field names in a 'Measured' record, in the order in which they--- appear.-measureKeys :: [String]-measureKeys = map fst measureAccessors_---- | Field names and accessors for a 'Measured' record.-measureAccessors :: Map String (Measured -> Maybe Double, String)-measureAccessors = fromList measureAccessors_---- | Normalise every measurement as if 'measIters' was 1.------ ('measIters' itself is left unaffected.)-rescale :: Measured -> Measured-rescale m@Measured{..} = m {-      measTime               = d measTime-    , measCpuTime            = d measCpuTime-    , measCycles             = i measCycles-    -- skip measIters-    , measNumGcs             = i measNumGcs-    , measBytesCopied        = i measBytesCopied-    , measMutatorWallSeconds = d measMutatorWallSeconds-    , measMutatorCpuSeconds  = d measMutatorCpuSeconds-    , measGcWallSeconds      = d measGcWallSeconds-    , measGcCpuSeconds       = d measGcCpuSeconds-    } where-        d k = maybe k (/ iters) (fromDouble k)-        i k = maybe k (round . (/ iters)) (fromIntegral <$> fromInt k)-        iters               = fromIntegral measIters :: Double---- | Convert a (possibly unavailable) GC measurement to a true value.--- If the measurement is a huge negative number that corresponds to--- \"no data\", this will return 'Nothing'.-fromInt :: Int64 -> Maybe Int64-fromInt i | i == minBound = Nothing-          | otherwise     = Just i---- | Convert from a true value back to the packed representation used--- for GC measurements.-toInt :: Maybe Int64 -> Int64-toInt Nothing  = minBound-toInt (Just i) = i---- | Convert a (possibly unavailable) GC measurement to a true value.--- If the measurement is a huge negative number that corresponds to--- \"no data\", this will return 'Nothing'.-fromDouble :: Double -> Maybe Double-fromDouble d | isInfinite d || isNaN d = Nothing-             | otherwise               = Just d---- | Convert from a true value back to the packed representation used--- for GC measurements.-toDouble :: Maybe Double -> Double-toDouble Nothing  = -1/0-toDouble (Just d) = d--instance Binary Measured where-    put Measured{..} = do-      put measTime; put measCpuTime; put measCycles; put measIters-      put measAllocated; put measNumGcs; put measBytesCopied-      put measMutatorWallSeconds; put measMutatorCpuSeconds-      put measGcWallSeconds; put measGcCpuSeconds-    get = Measured <$> get <*> get <*> get <*> get-                   <*> get <*> get <*> get <*> get <*> get <*> get <*> get---- | Apply an argument to a function, and evaluate the result to--- normal form (NF).-nf :: NFData b => (a -> b) -> a -> Benchmarkable-nf f x = toBenchmarkable (nf' rnf f x)---- | Apply an argument to a function, and evaluate the result to weak--- head normal form (WHNF).-whnf :: (a -> b) -> a -> Benchmarkable-whnf f x = toBenchmarkable (whnf' f x)---- | Perform an action, then evaluate its result to normal form.--- This is particularly useful for forcing a lazy 'IO' action to be--- completely performed.-nfIO :: NFData a => IO a -> Benchmarkable-nfIO a = toBenchmarkable (nfIO' rnf a)---- | Perform an action, then evaluate its result to weak head normal--- form (WHNF).  This is useful for forcing an 'IO' action whose result--- is an expression to be evaluated down to a more useful value.-whnfIO :: IO a -> Benchmarkable-whnfIO a = toBenchmarkable (whnfIO' a)---- Along with nf' and whnf', the following two functions are the core--- benchmarking loops. They have been carefully constructed to avoid--- allocation while also evaluating @a@.------ These functions must not be inlined. There are two possible issues that--- can arise if they are inlined. First, the work is often floated out of--- the loop, which creates a nonsense benchmark. Second, the benchmark code--- itself could be changed by the user's optimization level. By marking them--- @NOINLINE@, the core benchmark code is always the same.------ See #183 and #184 for discussion.---- | Generate a function that will run an action a given number of times,--- reducing it to normal form each time.-nfIO' :: (a -> b) -> IO a -> (Int64 -> IO ())-nfIO' reduce a = go-  where go n-          | n <= 0    = return ()-          | otherwise = do-              x <- a-              reduce x `seq` go (n-1)-{-# NOINLINE nfIO' #-}---- | Generate a function that will run an action a given number of times.-whnfIO' :: IO a -> (Int64 -> IO ())-whnfIO' a = go-  where-    go n | n <= 0    = return ()-         | otherwise = do-             x <- a-             x `seq` go (n-1)-{-# NOINLINE whnfIO' #-}---- | Specification of a collection of benchmarks and environments. A--- benchmark may consist of:------ * An environment that creates input data for benchmarks, created---   with 'env'.------ * A single 'Benchmarkable' item with a name, created with 'bench'.------ * A (possibly nested) group of 'Benchmark's, created with 'bgroup'.-data Benchmark where-    Environment  :: NFData env-                 => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark-    Benchmark    :: String -> Benchmarkable -> Benchmark-    BenchGroup   :: String -> [Benchmark] -> Benchmark---- | Run a benchmark (or collection of benchmarks) in the given--- environment.  The purpose of an environment is to lazily create--- input data to pass to the functions that will be benchmarked.------ A common example of environment data is input that is read from a--- file.  Another is a large data structure constructed in-place.------ __Motivation.__ In earlier versions of criterion, all benchmark--- inputs were always created when a program started running.  By--- deferring the creation of an environment when its associated--- benchmarks need the its, we avoid two problems that this strategy--- caused:------ * Memory pressure distorted the results of unrelated benchmarks.---   If one benchmark needed e.g. a gigabyte-sized input, it would---   force the garbage collector to do extra work when running some---   other benchmark that had no use for that input.  Since the data---   created by an environment is only available when it is in scope,---   it should be garbage collected before other benchmarks are run.------ * The time cost of generating all needed inputs could be---   significant in cases where no inputs (or just a few) were really---   needed.  This occurred often, for instance when just one out of a---   large suite of benchmarks was run, or when a user would list the---   collection of benchmarks without running any.------ __Creation.__ An environment is created right before its related--- benchmarks are run.  The 'IO' action that creates the environment--- is run, then the newly created environment is evaluated to normal--- form (hence the 'NFData' constraint) before being passed to the--- function that receives the environment.------ __Complex environments.__ If you need to create an environment that--- contains multiple values, simply pack the values into a tuple.------ __Lazy pattern matching.__ In situations where a \"real\"--- environment is not needed, e.g. if a list of benchmark names is--- being generated, a value which throws an exception will be passed--- to the function that receives the environment.  This avoids the--- overhead of generating an environment that will not actually be--- used.------ The function that receives the environment must use lazy pattern--- matching to deconstruct the tuple (e.g., @~(x, y)@, not @(x, y)@),--- as use of strict pattern matching will cause a crash if an--- exception-throwing value is passed in.------ __Example.__ This program runs benchmarks in an environment that--- contains two values.  The first value is the contents of a text--- file; the second is a string.  Pay attention to the use of a lazy--- pattern to deconstruct the tuple in the function that returns the--- benchmarks to be run.------ > setupEnv = do--- >   let small = replicate 1000 (1 :: Int)--- >   big <- map length . words <$> readFile "/usr/dict/words"--- >   return (small, big)--- >--- > main = defaultMain [--- >    -- notice the lazy pattern match here!--- >    env setupEnv $ \ ~(small,big) -> bgroup "main" [--- >    bgroup "small" [--- >      bench "length" $ whnf length small--- >    , bench "length . filter" $ whnf (length . filter (==1)) small--- >    ]--- >  ,  bgroup "big" [--- >      bench "length" $ whnf length big--- >    , bench "length . filter" $ whnf (length . filter (==1)) big--- >    ]--- >  ] ]------ __Discussion.__ The environment created in the example above is--- intentionally /not/ ideal.  As Haskell's scoping rules suggest, the--- variable @big@ is in scope for the benchmarks that use only--- @small@.  It would be better to create a separate environment for--- @big@, so that it will not be kept alive while the unrelated--- benchmarks are being run.-env :: NFData env =>-       IO env-    -- ^ Create the environment.  The environment will be evaluated to-    -- normal form before being passed to the benchmark.-    -> (env -> Benchmark)-    -- ^ Take the newly created environment and make it available to-    -- the given benchmarks.-    -> Benchmark-env alloc = Environment alloc noop---- | Same as `env`, but but allows for an additional callback--- to clean up the environment. Resource clean up is exception safe, that is,--- it runs even if the 'Benchmark' throws an exception.-envWithCleanup-    :: NFData env-    => IO env-    -- ^ Create the environment.  The environment will be evaluated to-    -- normal form before being passed to the benchmark.-    -> (env -> IO a)-    -- ^ Clean up the created environment.-    -> (env -> Benchmark)-    -- ^ Take the newly created environment and make it available to-    -- the given benchmarks.-    -> Benchmark-envWithCleanup = Environment---- | Create a Benchmarkable where a fresh environment is allocated for every--- batch of runs of the benchmarkable.------ The environment is evaluated to normal form before the benchmark is run.------ When using 'whnf', 'whnfIO', etc. Criterion creates a 'Benchmarkable'--- whichs runs a batch of @N@ repeat runs of that expressions. Criterion may--- run any number of these batches to get accurate measurements. Environments--- created by 'env' and 'envWithCleanup', are shared across all these batches--- of runs.------ This is fine for simple benchmarks on static input, but when benchmarking--- IO operations where these operations can modify (and especially grow) the--- environment this means that later batches might have their accuracy effected--- due to longer, for example, longer garbage collection pauses.------ An example: Suppose we want to benchmark writing to a Chan, if we allocate--- the Chan using environment and our benchmark consists of @writeChan env ()@,--- the contents and thus size of the Chan will grow with every repeat. If--- Criterion runs a 1,000 batches of 1,000 repeats, the result is that the--- channel will have 999,000 items in it by the time the last batch is run.--- Since GHC GC has to copy the live set for every major GC this means our last--- set of writes will suffer a lot of noise of the previous repeats.------ By allocating a fresh environment for every batch of runs this function--- should eliminate this effect.-perBatchEnv-    :: (NFData env, NFData b)-    => (Int64 -> IO env)-    -- ^ Create an environment for a batch of N runs. The environment will be-    -- evaluated to normal form before running.-    -> (env -> IO b)-    -- ^ Function returning the IO action that should be benchmarked with the-    -- newly generated environment.-    -> Benchmarkable-perBatchEnv alloc = perBatchEnvWithCleanup alloc (const noop)---- | Same as `perBatchEnv`, but but allows for an additional callback--- to clean up the environment. Resource clean up is exception safe, that is,--- it runs even if the 'Benchmark' throws an exception.-perBatchEnvWithCleanup-    :: (NFData env, NFData b)-    => (Int64 -> IO env)-    -- ^ Create an environment for a batch of N runs. The environment will be-    -- evaluated to normal form before running.-    -> (Int64 -> env -> IO ())-    -- ^ Clean up the created environment.-    -> (env -> IO b)-    -- ^ Function returning the IO action that should be benchmarked with the-    -- newly generated environment.-    -> Benchmarkable-perBatchEnvWithCleanup alloc clean work-    = Benchmarkable alloc clean (nfIO' rnf . work) False---- | Create a Benchmarkable where a fresh environment is allocated for every--- run of the operation to benchmark. This is useful for benchmarking mutable--- operations that need a fresh environment, such as sorting a mutable Vector.------ As with 'env' and 'perBatchEnv' the environment is evaluated to normal form--- before the benchmark is run.------ This introduces extra noise and result in reduce accuracy compared to other--- Criterion benchmarks. But allows easier benchmarking for mutable operations--- than was previously possible.-perRunEnv-    :: (NFData env, NFData b)-    => IO env-    -- ^ Action that creates the environment for a single run.-    -> (env -> IO b)-    -- ^ Function returning the IO action that should be benchmarked with the-    -- newly genereted environment.-    -> Benchmarkable-perRunEnv alloc = perRunEnvWithCleanup alloc noop---- | Same as `perRunEnv`, but but allows for an additional callback--- to clean up the environment. Resource clean up is exception safe, that is,--- it runs even if the 'Benchmark' throws an exception.-perRunEnvWithCleanup-    :: (NFData env, NFData b)-    => IO env-    -- ^ Action that creates the environment for a single run.-    -> (env -> IO ())-    -- ^ Clean up the created environment.-    -> (env -> IO b)-    -- ^ Function returning the IO action that should be benchmarked with the-    -- newly genereted environment.-    -> Benchmarkable-perRunEnvWithCleanup alloc clean work = bm { perRun = True }-  where-    bm = perBatchEnvWithCleanup (const alloc) (const clean) work---- | Create a single benchmark.-bench :: String                 -- ^ A name to identify the benchmark.-      -> Benchmarkable          -- ^ An activity to be benchmarked.-      -> Benchmark-bench = Benchmark---- | Group several benchmarks together under a common name.-bgroup :: String                -- ^ A name to identify the group of benchmarks.-       -> [Benchmark]           -- ^ Benchmarks to group under this name.-       -> Benchmark-bgroup = BenchGroup---- | Add the given prefix to a name.  If the prefix is empty, the name--- is returned unmodified.  Otherwise, the prefix and name are--- separated by a @\'\/\'@ character.-addPrefix :: String             -- ^ Prefix.-          -> String             -- ^ Name.-          -> String-addPrefix ""  desc = desc-addPrefix pfx desc = pfx ++ '/' : desc---- | Retrieve the names of all benchmarks.  Grouped benchmarks are--- prefixed with the name of the group they're in.-benchNames :: Benchmark -> [String]-benchNames (Environment _ _ b) = benchNames (b fakeEnvironment)-benchNames (Benchmark d _)   = [d]-benchNames (BenchGroup d bs) = map (addPrefix d) . concatMap benchNames $ bs--instance Show Benchmark where-    show (Environment _ _ b) = "Environment _ _" ++ show (b fakeEnvironment)-    show (Benchmark d _)   = "Benchmark " ++ show d-    show (BenchGroup d _)  = "BenchGroup " ++ show d--measure :: (U.Unbox a) => (Measured -> a) -> V.Vector Measured -> U.Vector a-measure f v = U.convert . V.map f $ v  -- | Outliers from sample data, calculated using the boxplot -- technique.
− Criterion/Types/Internal.hs
@@ -1,60 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# OPTIONS_GHC -fno-full-laziness #-}--- |--- Module      : Criterion.Types.Internal--- Copyright   : (c) 2017 Ryan Scott------ License     : BSD-style--- Maintainer  : bos@serpentine.com--- Stability   : experimental--- Portability : GHC------ Exports 'fakeEnvironment'.-module Criterion.Types.Internal (fakeEnvironment, nf', whnf') where--import Data.Int (Int64)---- | A dummy environment that is passed to functions that create benchmarks--- from environments when no concrete environment is available.-fakeEnvironment :: env-fakeEnvironment = error $ unlines-  [ "Criterion atttempted to retrieve a non-existent environment!"-  , "\tPerhaps you forgot to use lazy pattern matching in a function which"-  , "\tconstructs benchmarks from an environment?"-  , "\t(see the documentation for `env` for details)"-  ]---- Along with Criterion.Types.nfIO' and Criterion.Types.whnfIO', the following--- two functions are the core benchmarking loops. They have been carefully--- constructed to avoid allocation while also evaluating @f x@.------ Because these functions are pure, GHC is particularly smart about optimizing--- them. We must turn off @-ffull-laziness@ to prevent the computation from--- being floated out of the loop.------ For a similar reason, these functions must not be inlined. There are two--- possible issues that can arise if they are inlined. First, the work is often--- floated out of the loop, which creates a nonsense benchmark. Second, the--- benchmark code itself could be changed by the user's optimization level. By--- marking them @NOINLINE@, the core benchmark code is always the same.------ See #183 and #184 for discussion.---- | Generate a function which applies an argument to a function a--- given number of times, reducing the result to normal form.-nf' :: (b -> ()) -> (a -> b) -> a -> (Int64 -> IO ())-nf' reduce f x = go-  where-    go n | n <= 0    = return ()-         | otherwise = let !y = f x-                       in reduce y `seq` go (n-1)-{-# NOINLINE nf' #-}---- | Generate a function which applies an argument to a function a--- given number of times.-whnf' :: (a -> b) -> a -> (Int64 -> IO ())-whnf' f x = go-  where-    go n | n <= 0    = return ()-         | otherwise = f x `seq` go (n-1)-{-# NOINLINE whnf' #-}
− cbits/cycles.c
@@ -1,57 +0,0 @@-#include "Rts.h"--#if x86_64_HOST_ARCH || i386_HOST_ARCH--StgWord64 criterion_rdtsc(void)-{-  StgWord32 hi, lo;-  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));-  return ((StgWord64) lo) | (((StgWord64) hi)<<32);-}--#elif linux_HOST_OS--/*- * This should work on all Linux.- *- * Technique by Austin Seipp found here:- *- * http://neocontra.blogspot.com/2013/05/user-mode-performance-counters-for.html- */--#include <unistd.h>-#include <asm-generic/unistd.h>-#include <linux/perf_event.h>--static int fddev = -1;-__attribute__((constructor))-static void-init(void)-{-  static struct perf_event_attr attr;-  attr.type = PERF_TYPE_HARDWARE;-  attr.config = PERF_COUNT_HW_CPU_CYCLES;-  fddev = syscall (__NR_perf_event_open, &attr, 0, -1, -1, 0);-}--__attribute__((destructor))-static void-fini(void)-{-  close(fddev);-}--StgWord64-criterion_rdtsc (void)-{-  StgWord64 result = 0;-  if (read (fddev, &result, sizeof(result)) < sizeof(result))-    return 0;-  return result;-}--#else--#error Unsupported OS/architecture/compiler!--#endif
− cbits/time-osx.c
@@ -1,35 +0,0 @@-#include <mach/mach.h>-#include <mach/mach_time.h>--static mach_timebase_info_data_t timebase_info;-static double timebase_recip;--void criterion_inittime(void)-{-    if (timebase_recip == 0) {-	mach_timebase_info(&timebase_info);-	timebase_recip = (timebase_info.denom / timebase_info.numer) / 1e9;-    }-}--double criterion_gettime(void)-{-    return mach_absolute_time() * timebase_recip;-}--static double to_double(time_value_t time)-{-    return time.seconds + time.microseconds / 1e6;-}--double criterion_getcputime(void)-{-    struct task_thread_times_info thread_info_data;-    mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;-    kern_return_t kr = task_info(mach_task_self(),-				 TASK_THREAD_TIMES_INFO,-				 (task_info_t) &thread_info_data,-				 &thread_info_count);-    return (to_double(thread_info_data.user_time) +-	    to_double(thread_info_data.system_time));-}
− cbits/time-posix.c
@@ -1,24 +0,0 @@-#include <time.h>--void criterion_inittime(void)-{-}--double criterion_gettime(void)-{-    struct timespec ts;--    clock_gettime(CLOCK_MONOTONIC, &ts);--    return ts.tv_sec + ts.tv_nsec * 1e-9;-}---double criterion_getcputime(void)-{-    struct timespec ts;--    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);--    return ts.tv_sec + ts.tv_nsec * 1e-9;-}
− cbits/time-windows.c
@@ -1,80 +0,0 @@-/*- * Windows has the most amazingly cretinous time measurement APIs you- * can possibly imagine.- *- * Our first possibility is GetSystemTimeAsFileTime, which updates at- * roughly 60Hz, and is hence worthless - we'd have to run a- * computation for tens or hundreds of seconds to get a trustworthy- * number.- *- * Alternatively, we can use QueryPerformanceCounter, which has- * undefined behaviour under almost all interesting circumstances- * (e.g. multicore systems, CPU frequency changes). But at least it- * increments reasonably often.- */--#include <windows.h>--#if 0--void criterion_inittime(void)-{-}--double criterion_gettime(void)-{-    FILETIME ft;-    ULARGE_INTEGER li;--    GetSystemTimeAsFileTime(&ft);-    li.LowPart = ft.dwLowDateTime;-    li.HighPart = ft.dwHighDateTime;--    return (li.QuadPart - 130000000000000000ull) * 1e-7;-}--#else--static double freq_recip;-static LARGE_INTEGER firstClock;--void criterion_inittime(void)-{-    LARGE_INTEGER freq;--    if (freq_recip == 0) {-	QueryPerformanceFrequency(&freq);-	QueryPerformanceCounter(&firstClock);-	freq_recip = 1.0 / freq.QuadPart;-    }-}--double criterion_gettime(void)-{-    LARGE_INTEGER li;--    QueryPerformanceCounter(&li);--    return ((double) (li.QuadPart - firstClock.QuadPart)) * freq_recip;-}--#endif--static ULONGLONG to_quad_100ns(FILETIME ft)-{-    ULARGE_INTEGER li;-    li.LowPart = ft.dwLowDateTime;-    li.HighPart = ft.dwHighDateTime;-    return li.QuadPart;-}--double criterion_getcputime(void)-{-    FILETIME creation, exit, kernel, user;-    ULONGLONG time;--    GetProcessTimes(GetCurrentProcess(), &creation, &exit, &kernel, &user);--    time = to_quad_100ns(user) + to_quad_100ns(kernel);-    return time / 1e7;-}
changelog.md view
@@ -1,4 +1,21 @@+1.5.0.0++* Move the measurement functionality of `criterion` into a standalone package,+  `criterion-measurement`. In particular, `cbits/` and `Criterion.Measurement`+  are now in `criterion-measurement`, along with the relevant definitions of+  `Criterion.Types` and `Criterion.Types.Internal` (both of which are now under+  the `Criterion.Measurement.*` namespace).+  Consequently, `criterion` now depends on `criterion-measurement`.++  This will let other libraries (e.g. alternative statistical analysis+  front-ends) to import the measurement functionality alone as a lightweight+  dependency.++* Fix a bug on macOS and Windows where using `runAndAnalyse` and other+  lower-level benchmarking functions would result in an infinite loop.+ 1.4.1.0+ * Use `base-compat-batteries`.  1.4.0.0
criterion.cabal view
@@ -1,5 +1,5 @@ name:           criterion-version:        1.4.1.0+version:        1.5.0.0 synopsis:       Robust, reliable performance measurement and analysis license:        BSD3 license-file:   LICENSE@@ -24,7 +24,8 @@   GHC==7.10.3,   GHC==8.0.2,   GHC==8.2.2,-  GHC==8.4.1+  GHC==8.4.3,+  GHC==8.6.1  data-files:   templates/*.css@@ -65,25 +66,13 @@     Criterion.Internal     Criterion.Main     Criterion.Main.Options-    Criterion.Measurement     Criterion.Monad     Criterion.Report     Criterion.Types    other-modules:     Criterion.Monad.Internal-    Criterion.Types.Internal -  c-sources: cbits/cycles.c-  if os(darwin)-    c-sources: cbits/time-osx.c-  else {-    if os(windows)-      c-sources: cbits/time-windows.c-    else-      c-sources: cbits/time-posix.c-  }-   other-modules:     Paths_criterion @@ -97,6 +86,7 @@     cassava >= 0.3.0.0,     code-page,     containers,+    criterion-measurement,     deepseq >= 1.1.0.0,     directory,     exceptions >= 0.8.2 && < 0.11,@@ -221,3 +211,4 @@ source-repository head   type:     git   location: https://github.com/bos/criterion.git+  subdir:   criterion-measurement
examples/Overhead.hs view
@@ -11,6 +11,7 @@  main :: IO () main = do+  M.initializeTime -- Need to do this before calling M.getTime   statsEnabled <- getRTSStatsEnabled   defaultMain $ [       bench "measure" $            whnfIO (M.measure (whnfIO $ return ()) 1)
examples/criterion-examples.cabal view
@@ -17,8 +17,12 @@   GHC==7.10.3,   GHC==8.0.2,   GHC==8.2.2,-  GHC==8.4.1+  GHC==8.4.3,+  GHC==8.6.1 +flag conduit-vs-pipes+  default: True+ flag maps   default: True @@ -31,11 +35,14 @@     criterion  executable conduit-vs-pipes+  if !flag(conduit-vs-pipes)+    buildable: False+   main-is: ConduitVsPipes.hs    ghc-options: -Wall -rtsopts   build-depends:-    base == 4.*,+    base >= 4.8 && < 5,     conduit >= 1.2.13.1,     criterion,     pipes >= 4.3.5,@@ -67,7 +74,8 @@   ghc-options: -Wall -rtsopts   build-depends:     base == 4.*,-    criterion+    criterion,+    criterion-measurement  executable bad-read-file   main-is: BadReadFile.hs