criterion-0.4.1.0: Robust, reliable performance measurement and analysisSource codeContentsIndex
Criterion.Main
PortabilityGHC
Stabilityexperimental
Maintainerbos@serpentine.com
Contents
How to write benchmarks
Benchmarking IO actions
Benchmarking pure code
Fully evaluating a result
Types
Constructing benchmarks
Running benchmarks
Other useful code
Description
Wrappers for compiling and running benchmarks quickly and easily. See defaultMain below for an example.
Synopsis
class Benchmarkable a where
run :: a -> Int -> IO ()
data Benchmark
data Pure
bench :: Benchmarkable b => String -> b -> Benchmark
bgroup :: String -> [Benchmark] -> Benchmark
nf :: NFData b => (a -> b) -> a -> Pure
nfIO :: NFData a => IO a -> IO ()
whnf :: (a -> b) -> a -> Pure
defaultMain :: [Benchmark] -> IO ()
defaultMainWith :: Config -> Criterion () -> [Benchmark] -> IO ()
defaultOptions :: [OptDescr (IO Config)]
parseArgs :: Config -> [OptDescr (IO Config)] -> [String] -> IO (Config, [String])
How to write benchmarks

The Benchmarkable typeclass represents the class of all code that can be benchmarked. Every instance must run a benchmark a given number of times. We are most interested in benchmarking two things:

  • IO actions. Any IO action can be benchmarked directly.
  • Pure functions. GHC optimises aggressively when compiling with -O, so it is easy to write innocent-looking benchmark code that doesn't measure the performance of a pure function at all. We work around this by benchmarking both a function and its final argument together.
Benchmarking IO actions

Any IO action can be benchmarked easily if its type resembles this:

 IO a
Benchmarking pure code

Because GHC optimises aggressively when compiling with -O, it is potentially easy to write innocent-looking benchmark code that will only be evaluated once, for which all but the first iteration of the timing loop will be timing the cost of doing nothing.

To work around this, we provide a special type, Pure, for benchmarking pure code. Values of this type are constructed using one of two functions.

The first is a function which will cause results to be evaluated to head normal form (NF):

 nf :: NFData b => (a -> b) -> a -> Pure

The second will cause results to be evaluated to weak head normal form (the Haskell default):

 whnf :: (a -> b) -> a -> Pure

As both of these types suggest, when you want to benchmark a function, you must supply two values:

  • The first element is the function, saturated with all but its last argument.
  • The second element is the last argument to the function.

Here is an example that makes the use of these functions clearer. Suppose we want to benchmark the following function:

 firstN :: Int -> [Int]
 firstN k = take k [(0::Int)..]

So in the easy case, we construct a benchmark as follows:

 nf firstN 1000

The compiler will correctly infer that the number 1000 must have the type Int, and the type of the expression is Pure.

Fully evaluating a result

The whnf harness for evaluating a pure function only evaluates the result to weak head normal form (WHNF). If you need the result evaluated all the way to normal form, use the nf function to force its complete evaluation.

Using the firstN example from earlier, to naive eyes it might appear that the following code ought to benchmark the production of the first 1000 list elements:

 whnf firstN 1000

Because in this case the result will only be forced until it reaches WHNF, what this would actually benchmark is merely the production of the first list element!

Types
class Benchmarkable a whereSource
A benchmarkable function or action.
Methods
runSource
:: aThe function or action to benchmark.
-> IntThe number of times to run or evaluate it.
-> IO ()
Run a function or action the specified number of times.
show/hide Instances
data Benchmark Source
A benchmark may consist of either a single Benchmarkable item with a name, created with bench, or a (possibly nested) group of Benchmarks, created with bgroup.
show/hide Instances
data Pure Source
A container for a pure function to benchmark, and an argument to supply to it each time it is evaluated.
show/hide Instances
Constructing benchmarks
benchSource
:: Benchmarkable b
=> String
-> b
-> Benchmark
Create a single benchmark.
bgroupSource
:: StringA name to identify the group of benchmarks.
-> [Benchmark]Benchmarks to group under this name.
-> Benchmark
Group several benchmarks together under a common name.
nf :: NFData b => (a -> b) -> a -> PureSource
Apply an argument to a function, and evaluate the result to head normal form (NF).
nfIO :: NFData a => IO a -> IO ()Source
Perform an action, then evaluate its result to head normal form. This is particularly useful for forcing a lazy IO action to be completely performed.
whnf :: (a -> b) -> a -> PureSource
Apply an argument to a function, and evaluate the result to weak head normal form (WHNF).
Running benchmarks
defaultMain :: [Benchmark] -> IO ()Source

An entry point that can be used as a main function.

 import Criterion.Main

 fib :: Int -> Int
 fib 0 = 0
 fib 1 = 1
 fib n = fib (n-1) + fib (n-2)

 main = defaultMain [
        bgroup "fib" [ bench "fib 10" $ B fib 10
                     , bench "fib 35" $ B fib 35
                     , bench "fib 37" $ B fib 37
                     ]
                    ]
defaultMainWithSource
:: Config
-> Criterion ()Prepare data prior to executing the first benchmark.
-> [Benchmark]
-> IO ()

An entry point that can be used as a main function, with configurable defaults.

Example:

 import Criterion.Config
 import qualified Criterion.MultiMap as M

 myConfig = defaultConfig {
              -- Always display an 800x600 window with curves.
              cfgPlot = M.singleton KernelDensity (Window 800 600)
            }
 
 main = defaultMainWith myConfig (return ()) [
          bench "fib 30" $ B fib 30
        ]

If you save the above example as "Fib.hs", you should be able to compile it as follows:

 ghc -O --make Fib

Run "Fib --help" on the command line to get a list of command line options.

Other useful code
defaultOptions :: [OptDescr (IO Config)]Source
The standard options accepted on the command line.
parseArgs :: Config -> [OptDescr (IO Config)] -> [String] -> IO (Config, [String])Source
Parse command line options.
Produced by Haddock version 2.6.0