criterion-measurement-0.1.1.0: Criterion measurement functionality and associated types

Criterion.Measurement.Types

Description

Types for benchmarking.

The core type is Benchmarkable, which admits both pure functions and IO actions.

For a pure function of type a -> b, the benchmarking harness calls this function repeatedly, each time with a different Int64 argument (the number of times to run the function in a loop), and reduces the result the function returns to weak head normal form.

For an action of type IO a, the benchmarking harness calls the action repeatedly, but does not reduce the result.

Synopsis

Benchmark descriptions

data Benchmarkable #

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.

Constructors

 NFData a => Benchmarkable FieldsallocEnv :: Int64 -> IO a cleanEnv :: Int64 -> a -> IO () runRepeatedly :: a -> Int64 -> IO () perRun :: Bool

data Benchmark where #

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 Benchmarks, created with bgroup.

Constructors

 Environment :: NFData env => IO env -> (env -> IO a) -> (env -> Benchmark) -> Benchmark Benchmark :: String -> Benchmarkable -> Benchmark BenchGroup :: String -> [Benchmark] -> Benchmark
Instances
 # Instance detailsDefined in Criterion.Measurement.Types MethodsshowList :: [Benchmark] -> ShowS #

Measurements

data Measured #

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.

Constructors

 Measured FieldsmeasTime :: !DoubleTotal wall-clock time elapsed, in seconds.measCpuTime :: !DoubleTotal CPU time elapsed, in seconds. Includes both user and kernel (system) time.measCycles :: !Int64Cycles, in unspecified units that may be CPU cycles. (On i386 and x86_64, this is measured using the rdtsc instruction.)measIters :: !Int64Number 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.
Instances
 # Instance detailsDefined in Criterion.Measurement.Types Methods # Instance detailsDefined in Criterion.Measurement.Types Methodsgfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Measured -> c Measured #gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c Measured #dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c Measured) #dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Measured) #gmapT :: (forall b. Data b => b -> b) -> Measured -> Measured #gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Measured -> r #gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Measured -> r #gmapQ :: (forall d. Data d => d -> u) -> Measured -> [u] #gmapQi :: Int -> (forall d. Data d => d -> u) -> Measured -> u #gmapM :: Monad m => (forall d. Data d => d -> m d) -> Measured -> m Measured #gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Measured -> m Measured #gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Measured -> m Measured # # Instance detailsDefined in Criterion.Measurement.Types Methods # Instance detailsDefined in Criterion.Measurement.Types MethodsshowList :: [Measured] -> ShowS # # Instance detailsDefined in Criterion.Measurement.Types Associated Typestype Rep Measured :: Type -> Type # Methodsto :: Rep Measured x -> Measured # # Instance detailsDefined in Criterion.Measurement.Types MethodstoJSONList :: [Measured] -> Value #toEncodingList :: [Measured] -> Encoding # # Instance detailsDefined in Criterion.Measurement.Types Methods # Instance detailsDefined in Criterion.Measurement.Types Methodsput :: Measured -> Put #putList :: [Measured] -> Put # # Instance detailsDefined in Criterion.Measurement.Types Methodsrnf :: Measured -> () # type Rep Measured # Instance detailsDefined in Criterion.Measurement.Types type Rep Measured = D1 (MetaData "Measured" "Criterion.Measurement.Types" "criterion-measurement-0.1.1.0-7KG8K1NEehxHWLUX44t7se" False) (C1 (MetaCons "Measured" PrefixI True) (((S1 (MetaSel (Just "measTime") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Double) :*: S1 (MetaSel (Just "measCpuTime") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Double)) :*: (S1 (MetaSel (Just "measCycles") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int64) :*: (S1 (MetaSel (Just "measIters") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int64) :*: S1 (MetaSel (Just "measAllocated") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int64)))) :*: ((S1 (MetaSel (Just "measNumGcs") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int64) :*: (S1 (MetaSel (Just "measBytesCopied") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Int64) :*: S1 (MetaSel (Just "measMutatorWallSeconds") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Double))) :*: (S1 (MetaSel (Just "measMutatorCpuSeconds") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Double) :*: (S1 (MetaSel (Just "measGcWallSeconds") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 Double) :*: S1 (MetaSel (Just "measGcCpuSeconds") NoSourceUnpackedness SourceStrict DecidedStrict) (Rec0 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.

Convert from a true value back to the packed representation used for GC measurements.

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.

Convert from a true value back to the packed representation used for GC measurements.

Field names and accessors for a Measured record.

measureKeys :: [String] #

Field names in a Measured record, in the order in which they appear.

measure :: Unbox a => (Measured -> a) -> Vector Measured -> Vector a #

Normalise every measurement as if measIters was 1.

(measIters itself is left unaffected.)

Benchmark construction

Arguments

 :: 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

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.

Arguments

 :: 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

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.

Arguments

 :: (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

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.

Arguments

 :: (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

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.

Arguments

 :: (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

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.

Arguments

 :: (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

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.

toBenchmarkable :: (Int64 -> IO ()) -> Benchmarkable #

Construct a Benchmarkable value from an impure action, where the Int64 parameter indicates the number of times to run the action.

Arguments

 :: String A name to identify the benchmark. -> Benchmarkable An activity to be benchmarked. -> Benchmark

Create a single benchmark.

Arguments

 :: String A name to identify the group of benchmarks. -> [Benchmark] Benchmarks to group under this name. -> Benchmark

Group several benchmarks together under a common name.

Arguments

 :: String Prefix. -> String Name. -> String

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.

benchNames :: Benchmark -> [String] #

Retrieve the names of all benchmarks. Grouped benchmarks are prefixed with the name of the group they're in.

Evaluation control

nf :: NFData b => (a -> b) -> a -> Benchmarkable #

Apply an argument to a function, and evaluate the result to normal form (NF).

whnf :: (a -> b) -> a -> Benchmarkable #

Apply an argument to a function, and evaluate the result to weak head normal form (WHNF).

nfIO :: NFData a => IO a -> Benchmarkable #

Perform an action, then evaluate its result to normal form (NF). This is particularly useful for forcing a lazy IO action to be completely performed.

If the construction of the 'IO a' value is an important factor in the benchmark, it is best to use nfAppIO instead.

whnfIO :: IO a -> Benchmarkable #

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.

If the construction of the 'IO a' value is an important factor in the benchmark, it is best to use whnfAppIO instead.

nfAppIO :: NFData b => (a -> IO b) -> a -> Benchmarkable #

Apply an argument to a function which performs an action, then evaluate its result to normal form (NF). This function constructs the 'IO b' value on each iteration, similar to nf. This is particularly useful for IO actions where the bulk of the work is not bound by IO, but by pure computations that may optimize away if the argument is known statically, as in nfIO.

whnfAppIO :: (a -> IO b) -> a -> Benchmarkable #

Perform an action, then evaluate its result to weak head normal form (WHNF). This function constructs the 'IO b' value on each iteration, similar to whnf. This is particularly useful for IO actions where the bulk of the work is not bound by IO, but by pure computations that may optimize away if the argument is known statically, as in nfIO.