lean-bench

1.2. Optional per-param setup🔗

If your benchmarked function operates on a structure that's expensive to build (a sorted array, a tree, a hashmap), declaring it as Nat → α will fold the build cost into every timed iteration and ruin the verdict — a log n search behind a O(n) setup looks linear in the report. Use the optional with prep := <ident> clause to hoist the setup out of the timing loop:

def mkArr (n : Nat) : Array Nat := (Array.range n).map (· * 7) def prepInput (n : Nat) : Array Nat × Nat := (mkArr n, (n.max 1 - 1) * 7 / 2) partial def bsearch (a : Array Nat) (target : Nat) : Option Nat := Id.run do let mut lo := 0 let mut hi := a.size while lo < hi do let mid := (lo + hi) / 2 if a[mid]! < target then lo := mid + 1 else hi := mid if lo < a.size && a[lo]! == target then some lo else none def runBinary (input : Array Nat × Nat) : Nat := (bsearch input.1 input.2).getD 0 setup_benchmark runBinary n => Nat.log2 (n + 1) with prep := prepInput

Type contract: prep : Nat → σ and the benchmarked function has type σ → α. The prep's return type σ must be Hashable (Array Nat × Nat derives it automatically); the macro hashes the prep result before timing starts so its construction cost is fully paid up front. Prep then runs once per child-process spawn — not once per autotuner probe — and the inner loop calls the function with the prepared value.