criterion-0.4.0: Robust, reliable performance measurement and analysisSource codeContentsIndex
How to write benchmarks
Benchmarking IO actions
Benchmarking pure code
Fully evaluating a result
Constructing benchmarks
Running benchmarks
Other useful code
Wrappers for compiling and running benchmarks quickly and easily. See defaultMain below for an example.
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
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!

class Benchmarkable a whereSource
A benchmarkable function or action.
:: 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
:: Benchmarkable b
=> String
-> b
-> Benchmark
Create a single benchmark.
:: 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
whnf :: (a -> b) -> a -> PureSource
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
:: 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.


 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