Benchmarks
Bench blocks, language implementations, lifecycle hooks, overrides, and benchAsync
Bench blocks, language implementations, lifecycle hooks, overrides, and benchAsync
Bench blocks define the operations to measure. Each benchmark has per-language implementations — one expression or block per language — and can override suite-level settings, use lifecycle hooks, and optionally validate results.
1bench <name> {2 go: helperFunc(data)3 ts: helperFunc(data)4 rust: helper_func(&data)5}Only languages that have both a setup block and an implementation line in the bench block are executed. You can define benchmarks for a subset of languages and omit the rest.
You can write each language implementation as a single inline expression or as a multi-line block when you need multiple statements.
For simple operations, a single expression per language is enough:
1bench hashShort {2 go: keccak256Go(data)3 ts: keccak256Ts(data)4 rust: keccak256_rust(&data)5}When you need multiple statements, use a block:
1bench complexOp {2 go: {3 result := processData(data)4 _ = result5 }6 ts: {7 const result = processData(data)8 void result9 }10 rust: {11 let result = process_data(&data);12 let _ = result;13 }14}& when passing fixtures to functions that take &[u8].You can benchmark a subset of languages. Only languages that have both a setup block and an implementation line in the bench block will run; others are skipped.
1setup go { ... }2setup ts { ... }3# No setup rust block4
5bench operation {6 go: doSomething()7 ts: doSomething()8 # No rust: line — Rust is skipped9}Bench properties inherit from the suite when omitted. You can override them per benchmark when a specific bench needs different settings:
| Property | Description |
|---|---|
iterations | Fixed iteration count (when iterationBased) |
warmup | Warmup iterations |
targetTime | Target time (when timeBased) |
timeout | Per-benchmark timeout |
count | Number of timed runs |
cvThreshold | Coefficient of variation target (%) |
outlierDetection | IQR-based outlier removal |
sink | Black-box sink |
1bench expensiveOp {2 description: "Heavy computation"3 warmup: 5004 targetTime: 5000ms5 count: 36
7 go: heavyCompute(data)8 ts: heavyCompute(data)9 rust: heavy_compute(&data)10}Hooks run at specific points in the benchmark lifecycle. They are defined per language on individual bench blocks.
| Hook | When it runs |
|---|---|
before <lang> | Once before the timed loop starts |
each <lang> | Before each iteration, outside the timing window |
after <lang> | Once after the timed loop ends |
Use before for setup that should not be timed. Use each to reset mutable state between iterations. Use after for teardown or inspection. You can write hooks in flat syntax (one line per language) or grouped syntax (all languages under a single before/each/after block).
1bench withHooks {2 before go: resetCounter()3 before ts: resetCounter()4
5 each go: prepareIteration()6 each ts: prepareIteration()7
8 go: runBenchmark()9 ts: runBenchmark()10
11 after go: { _ = counter }12 after ts: { void counter }13}1bench withHooks {2 before: {3 go: resetCounter()4 ts: resetCounter()5 }6
7 each: {8 go: prepareIteration()9 ts: prepareIteration()10 }11
12 go: runBenchmark()13 ts: runBenchmark()14
15 after: {16 go: { _ = counter }17 ts: { void counter }18 }19}before go: resetState()) or a multi-line block (e.g. after go: { _ = x }).Bench blocks support a few additional fields for metadata and control:
| Field | Type | Description |
|---|---|---|
description | string | Human-readable description |
tags | string[] | Tags for filtering/grouping |
skip | per-lang bool | Skip this benchmark for specific languages |
validate | per-lang expr | Validate the return value |
1bench hashWithValidation {2 description: "Keccak256 with output check"3 tags: ["crypto", "hash"]4
5 skip: { go: false ts: false rust: true } # Skip Rust for this benchmark6 go: keccak256(data)7 ts: keccak256(data)8
9 validate: { go: len(result) == 32 ts: result.length === 32 }10}For async operations such as RPC calls, use benchAsync instead of bench. It uses async-sequential semantics: one awaited completion per iteration.
1benchAsync fetchBlock {2 description: "Fetch block number from RPC"3 targetTime: 5000ms4
5 go: getBlockNumber()6 ts: await getBlockNumber()7}benchAsync applies internal caps: warmup ≤ 5, samples ≤ 50. Async implementations should fail fast on errors (throw/panic) rather than returning error values.1bench keccak256Bench {2 description: "Keccak-256 hash"3 tags: ["crypto"]4 warmup: 1005 targetTime: 1000ms6
7 before go: { _ = data }8 before ts: { void data }9
10 go: keccak256Go(data)11 ts: keccak256Ts(data)12 rust: keccak256_rust(&data)13
14 after go: { _ = 0 }15 after ts: { void 0 }16
17 validate: { go: len(result) == 32 ts: result.length === 32 rust: result.len() == 32 }18}