motoko-benchmarks-generation
Motoko Benchmarks with bench‑helper
What This Is
bench-helper is a tiny Motoko library that standardizes how to write benchmarks.
You describe a benchmark using a small schema (name, rows, cols), provide a run(row, col)
function, and return a versioned bench record. Each file under bench/*.bench.mo defines one
benchmark module. A runner can then discover and execute all benches consistently.
Prerequisites
mops.toml (add dependencies and toolchain)
If you already have a mops.toml, just add bench-helper under [dev-dependencies].
If your project still uses mo:base instead of mo:core, you can keep it — benches themselves can be written with
mo:base without affecting your runtime canisters.
Directory & File Conventions
- Put benches under
bench/at the repo root. - Name files with the suffix
.bench.mo, one benchmark per file; for example:bench/base64.bench.mo. - Each bench file is a Motoko
module { ... }that exposes a singlepublic func init() : Bench.V1function. - Inside
init, construct aBench.Schemaand returnBench.V1(schema, run)whererun : (rowIndex : Nat, colIndex : Nat) -> ()performs the measured operation.
Minimal skeleton
import Array "mo:core/Array";
import Text "mo:core/Text";
import Bench "mo:bench-helper";
module {
public func init() : Bench.V1 {
let schema : Bench.Schema = {
name = "My bench";
description = "What this bench measures";
rows = ["size 16", "size 64", "size 256"]; // your row labels
columns = ["operation A", "operation B"]; // your column labels
};
// Prepare inputs outside of `run` so they are not re-created on every iteration
let inputs : [[Nat8]] = [
Array.init<Nat8>(16, 0),
Array.init<Nat8>(64, 0),
Array.init<Nat8>(256, 0),
];
// Build a table of routines to measure: routines[row][col] : () -> ()
let routines : [[() -> ()]] = Array.tabulate(
rows.size(),
func(ri) {
let input = inputs[ri]; // capture precomputed input
[
func() { ignore input.size() }, // operation A @ inputs[ri]
func() { ignore input.toArray() }, // operation B @ inputs[ri]
]
},
);
// The runner calls this many times; keep it tiny and branch-free.
Bench.V1(schema, func(ri : Nat, ci : Nat) = routines[ri][ci]());
};
};
Note: if you're not using "core" dependency, replace "mo:core" imports with "mo:base"
How It Works
- Schema
nameanddescriptiondescribe the bench.rowsenumerate different operations or variants you measure (e.g., "encode", "decode").colsenumerate different input categories (e.g., message sizes).
- Runner contract
- You return a versioned record
Bench.V1(schema, run); the runner callsrun(rowIndex, colIndex)many times to record timings. - Side effects/results inside
runshould be consumed (e.g.,ignore ...) to prevent dead‑code elimination.
- You return a versioned record
Common Pitfalls
- Rows/cols mismatch
- Ensure your
routinestable has dimensionsrows.size() x cols.size(). If you add or remove a row/col label, update how you buildroutines; otherwise some cells will be no‑ops.
- Ensure your
- Doing expensive setup inside
run- Generate inputs once in
initand capture them in closures. Only do the core operation inrun.
- Generate inputs once in
- Forgetting to consume results
- Use
ignoreto consume return values; otherwise the compiler might drop the call as dead code.
- Use
- Non‑determinism and timing noise
- Keep
runfree of logging/printing and random allocation; keep GC pressure comparable across rows.
- Keep
More examples
bench-helper reference benches: https://github.com/research-ag/bench-helper/tree/main/bench
Verify It Works
The exact runner/command may vary depending on your environment. After adding bench-helper to mops.toml and writing
.bench.mo files:
- Ensure your project resolves dependencies:
mops install - Run benchmarks:
mops bench - Consult the
bench-helperpackage README for the latest recommended runner command for your toolchain/version.
More from research-ag/motoko-skills
motoko-core-code-improvements
Optional, modular cleanups and style improvements to apply on new mo:core projects (or after mo:core migration). Covers import ordering, unused import cleanup, and single‑expression return removal, with detection checks and automation recipes.
33motoko-general-style-guidelines
Load when working with contents in *.mo files
32motoko-performance-optimizations
General performance optimization techniques for Motoko. Reducing allocations, efficient Text building, fixed-width arithmetic, block processing, async patterns, and more. Load when you need to improve hot paths or reduce overhead without changing behavior.
31motoko-dot-notation-migration
Use new dot-notation syntax in projects with mo:core dependency
31motoko-base-to-core-migration
Complete, AI-ready playbook to migrate Motoko projects from mo:base to mo:core — phases, renames, data structure changes, agent strategy, verification scripts, upgrade tests, and production rollout.
27motoko-compiler-warnings-fixes
Guidelines for fixing Motoko compiler warnings (moc). Use when asked to fix, suppress, or clean up Motoko compiler warnings from `dfx build --check`.
23