bench
Benchmarks, run by heff bench, as Go’s testing.B runs them.
A benchmark is a top-level definition of type Bench, found by its
type as a test is, and named by its groups’ names and its own. Its
body is given a count, n, and does the work measured n times;
heff bench runs it with 1, then with a count predicted from the run
before, until a run takes the bench time (a second, --time), and
says the last run: its count, and per time it did the work, its
nanoseconds, the bytes it made and how many objects.
import bench as Bench
fib : Int -> Int fib n = if n < 2 then n else fib (n - 1) + fib (n - 2)
fibs : Bench.Bench fibs = Bench.group “fib” [ Bench.bench “10” (\n -> Bench.times n (\i -> fib (Bench.keep 10))) , Bench.bench “20” (\n -> Bench.times n (\i -> fib (Bench.keep 20))) ]
The work’s result is kept (times keeps each, keep one): the
compiler drops what is computed and never used, and computes once
what is made of what it knows — fib 20 is the same each time, but
fib (keep 20) is computed each time, as keep 20 is not known.
What a body does before its \n -> is done once, when the benchmark
is made, not measured. A body may check what it computed (check’s
assert, failure, skip): a run that fails ends the benchmark,
which heff bench says failed.
heff bench builds a file’s benchmarks into a program through C, at
heff install’s level (benchMain), its objects counted as they are
made, and runs each benchmark in it.
keep
keep : {a : Type} -> a -> aThe value, as it is; but no pass of the compiler, nor the C compiler
after it, knows what it is, nor drops it, its result unused: what
a benchmark’s work is kept by, which the compiler would otherwise
drop, its result unused, or compute once, what it is made of known.
times keeps each result; keep the work’s arguments too (fib (keep 20), not computed once). It costs nothing: no instruction.
>>> keep 20 + 121Ran
data Ran = RanThrough | RanFailed Check.Failure | RanSkipped StringWhat came of a run of a benchmark: it went through, it failed (why), or it was skipped (why: what it needs is not there).
Sample
data Sample = MkSample Int Int IntWhat a run took: its nanoseconds, and the objects it made and their
bytes (0 where they are not counted, not built by heff bench).
TimerF
data TimerF (m : Type -> Type) x = StartTimer Int (Int -> x) | StopTimer Ran (Sample -> x)The timer, the world’s (its provider’s, in C): started with the run’s count, which it gives back, so that the work made of it comes after; stopped with what came of the run, so that the work comes before — what the run took.
timer
timer : HFunctorFor timing a benchmark: a timer started before a run and stopped after it, the run’s sample.
startTimer
startTimer : {e : HFunctor} -> {{tm : timer <: e}} -> Int -> Co e IntThe timer started, for a run of this count: the count, given back.
stopTimer
stopTimer : {e : HFunctor} -> {{tm : timer <: e}} -> Ran -> Co e SampleThe timer stopped, the run come to this: what it took.
Bench
data Bench = Benchmark String (Int -> Co Check.check Unit) | Group String (List Bench)A benchmark: its body, given the count, in checking’s effect; or benchmarks under a name.
bench
bench : String -> ({e : HFunctor} -> {{chk : Check.check <: e}} -> Int -> Co e Unit) -> BenchA benchmark: its body does the work as many times as it is given,
and may check what it computed. What it does before its \n -> is
done once, when the benchmark is made.
group
group : String -> List Bench -> BenchBenchmarks under a name.
times
times : {a : Type} -> {e : HFunctor} -> Int -> (Int -> a) -> Co e UnitThe work done n times, f 0 to f (n - 1), each result kept: a
benchmark’s body, as Go’s for i := 0; i < b.N; i++.
Step
data Step a = Goes a | Ends RanA run, as far as it went: going on, or ended (failed, skipped).
checker
checker : Handler Check.checkChecking, as a benchmark is run: a failure ends the run, as a skip does; a label and a note are passed over.
ranOf
ranOf : Step Unit -> RanrunOnce
runOnce : {e : HFunctor} -> {{tm : timer <: e}} -> (Int -> Co Check.check Unit) -> Int -> Co e (Ran, Sample)A run of the body, of this count, timed: the timer started, the body run on the count it gives back, and stopped with what came of it.
Goal
data Goal = ForNanos Int | ForCount IntHow long a benchmark is run: until a run takes these nanoseconds, its
count grown; or once, of this count (Go’s -benchtime 100x).
Measured
data Measured = MeasuredAt Int Sample | Unmeasured RanWhat came of a benchmark: its last run’s count and what it took, or a run that did not go through.
maxCount
maxCount : Intthe most a run counts: a billion, as Go’s
grownTo
grownTo : Int -> Int -> IntnextCount
nextCount : Int -> Int -> Int -> IntThe count of the next run, as Go predicts it: the goal over what the last took per time, a fifth more — but a hundred times the last at most, one more at least, and a billion at most.
>>> nextCount 1000000000 1 1000100>>> nextCount 1000000000 100 100000010000>>> nextCount 1000000000 10000 50000000240000>>> nextCount 1000000000 500000000 900000000666666666>>> nextCount 1000000000 7 0700grow
grow : {e : HFunctor} -> {{tm : timer <: e}} -> Int -> (Int -> Co Check.check Unit) -> Int -> (Ran, Sample) -> Co e MeasuredmeasuredOf
measuredOf : Int -> (Ran, Sample) -> Measuredmeasure
measure : {e : HFunctor} -> {{tm : timer <: e}} -> Goal -> (Int -> Co Check.check Unit) -> Co e MeasuredA benchmark measured, as Go measures one: run once, then again, its count grown, until a run takes the goal (or of the count given).
benchmarksUnder
benchmarksUnder : List String -> Bench -> List (List String, Int -> Co Check.check Unit)Each benchmark under these groups, by its names, its groups’ and its own, and its body.
benchmarksOf
benchmarksOf : List Bench -> List (List String, Int -> Co Check.check Unit)selectedNames
selectedNames : List Regex.Regex -> List Bench -> List (List String)The names of each benchmark the levels of a pattern select, as
heff test --run selects tests (check’s selectedBy).
sameNames
sameNames : List String -> List String -> BoolbenchmarkNamed
benchmarkNamed : List String -> List Bench -> Maybe (Int -> Co Check.check Unit)the benchmark of these names
failureText
failureText : Check.Failure -> StringmeasuredJson
measuredJson : Measured -> JSON.JsonWhat came of a benchmark, as its program says it: JSON, its count, nanoseconds, objects and bytes; or why it failed, or was skipped.
goalOf
goalOf : Int -> Int -> GoalmeasuredSaid
measuredSaid : {e : HFunctor} -> {{tm : timer <: e}} -> {{sys : Process.process <: e}} -> {{io : stdio <: e}} -> Goal -> Maybe (Int -> Co Check.check Unit) -> Co e Unitasked
asked : {e : HFunctor} -> {{tm : timer <: e}} -> {{sys : Process.process <: e}} -> {{io : stdio <: e}} -> List Bench -> List String -> Co e Unitmeasured
measured : HFunctorThe effects a file’s benchmarks are run in: the timer, the program’s arguments and its output.
Benchmarks
type Benchmarks = Co measured UnitWhat a file’s benchmarks are run by: benchMain’s.
benchMain
benchMain : List Bench -> BenchmarksThe program heff bench builds of a file’s benchmarks: asked for
them by its arguments, it says the names of those a pattern selects
(list PATTERN), or measures the one its names name (run NANOS COUNT NAME..., a count above 0 run once, else until a run takes
the nanoseconds) and says what came of it, a line of JSON.