mops bench
Run Motoko benchmarks.
mops bench [filter]
Put your benchmark code in *.bench.mo files inside a bench/ or benchmark/ directory (nested subdirectories work too). With a [filter] argument, every *<filter>*.mo file under those directories runs — the .bench.mo suffix is not required for filtered files.
Directories below the project root that are their own checkout — a git worktree, a submodule, a nested clone — are skipped, so a copy of the project checked out beside it does not contribute benchmarks.
It is necessary to use bench package to write benchmarks.
The output format is a markdown table, so you can copy-paste it into your README.md.
How it works
Benchmarks run on PocketIC, which Mops downloads and manages itself — dfx is not involved and does not need to be installed. Pin a version with mops toolchain use pocket-ic 15.0.0. There is no default: an unpinned project errors, unless MOPS_POCKET_IC_URL points at an already-running PocketIC server (Mops then attaches to it instead of spawning one, and an existing pin is ignored).
Under the hood, Mops will:
- Start a PocketIC server on an ephemeral port (or attach to the
MOPS_POCKET_IC_URLserver) - Wrap each
*.bench.mofile in a canister - Compile canisters under enhanced orthogonal persistence (moc's default) and deploy them. Only the legacy GCs of
moc1.x are compiled with the--force-gcflag - Before each cell, send empty update calls until no incremental GC cycle is in progress. While a cycle is in progress, the GC's write and allocation barriers are on, so a cell would cost more instructions depending on what ran before it
- Run each cell of the benchmark file as an update call (or a query call with
--query) - For each call measure usage of wasm instructions(
performance_counter) and heap growth(rts_heap_size). The GC does not run during the measurement, so heap growth is exactly what the call allocated. The Garbage Collection table, which would show 0, is not printed
The number you get is for the exact wasm PocketIC runs, and nothing post-optimizes it on deploy:
- With
[optimize]—mops benchrunswasm-opton the module before deploy (same pass asmops build). Prefer this when you want bench numbers to match an optimized deploy artifact. Pass--no-optimizeto skip the pass for a single run without editingmops.toml. - Without
[optimize]— PocketIC runs the rawmocoutput, with no optimization.
Always compare runs made with the same moc version, the same PocketIC version, and the same [optimize] settings. Different replicas report different instruction and heap counts, so baselines recorded before mops 3.0.0 on the dfx replica are not comparable — re-record them with --save.
[optimize] requires a [toolchain] wasm-opt pin, and a wasm-opt failure fails the run. Use --verbose for full wasm-opt output.