Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
de89731
Support loading options from a TOML config file
VisruthSK Aug 4, 2026
4df05e7
Add named benchmarks with per-benchmark overrides
VisruthSK Aug 4, 2026
f0d011e
Unify working-directory and env with the rest of the options
VisruthSK Aug 4, 2026
b93d3d0
Run every configured benchmark by default, with a compact summary
VisruthSK Aug 4, 2026
d296388
Make --benchmark take multiple names in one flag, not by repeating it
VisruthSK Aug 4, 2026
bd0f3b3
Avoid cloning environment variables on every benchmark run
VisruthSK Aug 4, 2026
92ff638
Write report_short.txt to the top-level output directory
VisruthSK Aug 4, 2026
eaddf3c
Reject configuration values with too many entries for a single-valued…
VisruthSK Aug 4, 2026
c29fe66
Use the parse/render example benchmarks in the report_short.txt sample
VisruthSK Aug 4, 2026
3648989
Split command between CLI and TOML cleanly in the docs
VisruthSK Aug 4, 2026
337613d
Tighten config resolution per clippy pedantic/nursery lints
VisruthSK Aug 4, 2026
4286571
Merge a benchmark's env table with the top-level one instead of repla…
VisruthSK Aug 4, 2026
1598238
Fix report_short.txt crashing when --output-dir overrides a configure…
VisruthSK Aug 4, 2026
fa5003b
Correct the README claim that the command cannot be configured
VisruthSK Aug 5, 2026
d9adcf0
Support loading options from a TOML config file
VisruthSK Aug 4, 2026
6fcdc5f
Add named benchmarks with per-benchmark overrides
VisruthSK Aug 4, 2026
ee4a41e
Unify working-directory and env with the rest of the options
VisruthSK Aug 4, 2026
9a34105
Run every configured benchmark by default, with a compact summary
VisruthSK Aug 4, 2026
05576b6
Make --benchmark take multiple names in one flag, not by repeating it
VisruthSK Aug 4, 2026
192721a
Avoid cloning environment variables on every benchmark run
VisruthSK Aug 4, 2026
d2f898a
Write report_short.txt to the top-level output directory
VisruthSK Aug 4, 2026
1e5d757
Reject configuration values with too many entries for a single-valued…
VisruthSK Aug 4, 2026
d2f00ed
Use the parse/render example benchmarks in the report_short.txt sample
VisruthSK Aug 4, 2026
7e68e65
Split command between CLI and TOML cleanly in the docs
VisruthSK Aug 4, 2026
abc19e3
Tighten config resolution per clippy pedantic/nursery lints
VisruthSK Aug 4, 2026
95b1831
Merge a benchmark's env table with the top-level one instead of repla…
VisruthSK Aug 4, 2026
5c4221c
Fix report_short.txt crashing when --output-dir overrides a configure…
VisruthSK Aug 4, 2026
acf369b
Correct the README claim that the command cannot be configured
VisruthSK Aug 5, 2026
216abfe
Cleanup
VisruthSK Aug 16, 2026
6d78b70
Merge branch 'toml' of https://github.com/VisruthSK/b3 into toml
VisruthSK Aug 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "b3"
version = "0.1.0"
edition = "2024"
rust-version = "1.85"
description = "Bayesian Branch Benchmarking: compares runtime and memory between two git revisions."
license = "MIT OR Apache-2.0"
repository = "https://github.com/VisruthSK/b3"
Expand All @@ -11,11 +12,18 @@ categories = ["command-line-utilities", "development-tools::profiling"]

[dependencies]
anyhow = "1.0.104"
clap = { version = "4.6.4", features = ["derive"] }
clap = { version = "4.6.4", features = ["derive", "string"] }
rand = "0.10.2"
rand_distr = "0.6.0"
serde_json = "1.0.149"
tempfile = "3.27.0"
toml = { version = "1.1.4", default-features = false, features = [
"std",
"parse",
"display",
"preserve_order",
"serde",
] }

[package.metadata.docs.rs]
rustdoc-args = ["--html-in-header", "katex-header.html"]
Expand Down
52 changes: 50 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,69 @@ NB: `b3` is currently experimental, the API may change without warning.
## Usage

```sh
b3 --baseline main --candidate HEAD --repetitions 30 --output-dir benchmark/ -- Rscript benchmark.R
b3 --baseline main --candidate HEAD --repetitions 30 --interval 0.5 0.8 0.98 --output-dir benchmark/ -- cargo bench
```

Run `b3 --help` for the full set of options.

## Configuration

The flags above can also be set in a TOML file, keyed by their long names. `b3` reads `b3.toml` from the working directory when present, or the file given by `--config`.

```toml
baseline = "main"
candidate = "HEAD"
repetitions = 30
interval = [0.5, 0.8, 0.98]
output-dir = "benchmark/"
```

With that file, the run above is `b3 -- cargo bench`. Arguments override the file, which overrides the built-in defaults. `b3 --help` always shows only the built-in CLI defaults. The command may also live in the file as a `command` list; one passed after `--` overrides it.

A `[benchmarks]` table is where commands belong in TOML. Each entry names a benchmark for `--benchmark` to select and must set its own `command`; it may override run options such as `repetitions`, `working-directory`, and `isolate`. `baseline`, `candidate`, and `seed` apply to the whole suite. A benchmark's `env` table is merged with the top-level one variable by variable, with the benchmark's values winning on conflicts:

```toml
repetitions = 10
draws = 20000

[benchmarks.parse]
command = ["cargo", "run", "--release", "--", "parse"]

[benchmarks.render]
repetitions = 50
working-directory = "benchmarks/render"
command = ["cargo", "run", "--release", "--", "render"]

[benchmarks.render.env]
RAYON_NUM_THREADS = "1"
```

`b3 --benchmark render` runs with 50 repetitions in `benchmarks/render`; `b3 --benchmark parse` runs with the top-level 10. An explicit argument still overrides a benchmark's setting.
`working-directory` must be a relative path within the worktree; absolute paths and `..` are rejected.

With no `--benchmark`, every benchmark in the table runs in declaration order, each in its own `--output-dir` subdirectory named after it. Pass `--benchmark render parse` to run only some of them in the order given. A configuration with no `[benchmarks]` table always runs a single, unnamed command, exactly as with no configuration file at all.

One seed is drawn for the suite when `seed` is omitted, recorded in every benchmark's `config.json`, and used for every benchmark's run schedule and bootstrap. Benchmarks share the same baseline and candidate worktrees by default. Set `isolate = true` on a benchmark that needs a fresh pair, or pass `--isolate` to isolate every selected benchmark.

The intended workflow is a `b3.toml` with named benchmarks, run with a plain `b3`; the trailing `-- <COMMAND>...` is for one-off runs that skip configuration entirely.

## Output

Each run writes to `--output-dir`:
Each run writes to its output directory:

- `config.json`: run's parameters and resolved revisions.
- `benchmark.log`: one JSON line per individual run.
- `measurements.csv`: paired baseline/candidate timings.
- `posterior.csv`: Bayesian bootstrap draws.
- `report.txt`: human-readable summary.

Running more than one benchmark also prints a one-line-per-benchmark summary and writes it to `report_short.txt` in `--output-dir`:

```
parse: 1.2s -> 554.0ms [-52.41%, -51.31%]
render: 3.1s -> 3.0s [-4.02%, +1.15%]
```

## License

Dual licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT) at your option.
Loading