jj / How jj bench works

How jj bench works

jj bench is a hidden group that times index and revset operations against a real repo with the Criterion benchmarking library. It exists only in builds with the bench feature.

On this page
  1. 1. Command-line surface
  2. 3. End-to-end flow
  3. 4. Core architecture touchpoints

In short: each subcommand loads the repo through the workspace helper, resolves its inputs once, runs the operation once and reports how long that took, then hands the same closure to Criterion, which samples it repeatedly and compares against a saved baseline. Default builds don't contain it: the feature is off by default.

The group is BenchCommand, help text "Commands for benchmarking internal operations", marked #[command(hide = true)]. It is the Bench variant of the top-level Command enum, compiled only under #[cfg(feature = "bench")] (source, dispatch), and cmd_bench matches on it. The feature only pulls in criterion and is not in default (source).

1. Command-line surface

SubcommandHelp textArgumentsWhat is timed
common-ancestors"Find the common ancestor(s) of a set of commits"REVISION1, REVISION2index.common_ancestors of the two commits (source)
is-ancestor"Checks if the first commit is an ancestor of the second commit"ANCESTOR, DESCENDANTindex.is_ancestor (source)
resolve-prefix"Resolve a commit ID prefix"PREFIX (hex)index.resolve_commit_id_prefix (source)
revset"Walk the revisions in the revset"REVISIONS… or -f FILE, one requiredresolving, evaluating and streaming each revset (source)

All four flatten the shared Criterion options (CriterionArgs):

FlagDefaultMeaning
-s, --save-baseline NAMEbase"Name of baseline to save results"
-b, --baseline NAMEnone"Name of baseline to compare with". Same group as --save-baseline, so they conflict; a missing baseline is not an error (source).
--sample-size N100"Sample size for the benchmarks, which must be at least 10"; clap rejects smaller values.

The global flags are on the CLI page.

3. End-to-end flow

jj bench: from subcommand to Criterion cmd_bench dispatches to a subcommand. It loads the repo with the workspace helper and resolves its inputs. new_criterion builds a Criterion instance with the UI's colour setting, the sample size, and either a baseline to save or one to compare with. run_bench times one call of the routine, prints the duration and result, then passes the routine to bench_function. bench revset instead builds a benchmark group and, for each revset, reloads the workspace without snapshotting before every sample so store caches are cold while the index stays preloaded. cmd_bench match BenchCommand → cmd_bench_* workspace_helper; resolve inputs build the routine closure over the index new_criterion colour, sample size, save or compare baseline run_bench "First run took …", then bench_function bench revset: bench_with_input fresh workspace per batch, index preloaded

4. Core architecture touchpoints

PieceRole here
IndexWhat three of the four benchmarks measure directly: common ancestors, ancestry, and prefix resolution, each called on workspace_command.repo().index().
Revsetsbench revset parses each expression once, then times resolution with a symbol resolver that has no extensions ("without parsing/evaluating short-prefixes"), evaluation, and counting the stream (source). With -f, blank lines and lines starting with # are skipped (source).
Store cachesBefore each bench revset batch the workspace is reloaded, because "repo.reload_at() isn't enough to clear store cache", and the index is preloaded with readonly_index (source).
Working copy, operation logOnly the normal workspace helper's snapshot on load; nothing is written by the benchmarks themselves.