std.bench

Microbenchmark measurement and reporting.

Importuse std.bench

Types

struct Runner#

The registration handle passed to fn bench(b: bench.Runner). Obtain one with bench.runner(); the driver's synthetic entry point does that for you.

use std.bench

fn sum_to(size: int) {
  let mut total = 0
  for value in 0..size { total += value }
  if total < 0 { println(total) }
}

fn bench(b: bench.Runner) {
  b.iter("small", || sum_to(1_000))
  b.iter_sized("sum", |size| sum_to(size), 100_000)
}
id: int

The driver's handle for this process's registry; not meant to be read.

Functions

fn runner() -> Runner#

Creates the runner, reading the role, case selection, size override, rep count and filter the driver passed in the environment. Called once per process, before bench(b).

async fn iter_async(self: Runner, name: string, body: fn() -> Future<int>)async#

Registers a case whose body is asynchronous — a free function rather than a Runner method, because moon's impl blocks do not accept async fn (the parser rejects it outright) and closures cannot await.

Both restrictions are worked around by the same shape: the closure does not await, it returns a Future (calling an async fn without awaiting is exactly that), and this function — which is free, so it may be async — is what awaits it. That is what makes an async workload expressible as a registered benchmark at all:

async fn bench(b: bench.Runner) { await std.bench.iter_async(b, "gc_async_channels", || pipeline(2400)) }

The cost is that the whole registration chain up to fn bench must be async and must await this call — moon bench synthesizes an async entry wrapper when the user's bench is async, so nothing else changes.

  • name: Case name, as --filter matches it.
  • body: Returns the future to await; it must not await itself.
use std.bench

async fn pipeline() -> int {
  await sleep(1)
  42
}

async fn bench(b: bench.Runner) {
  await std.bench.iter_async(b, "pipeline", || pipeline())
}