{"id":18812217,"url":"https://github.com/blobfolio/brunch","last_synced_at":"2025-04-10T00:18:38.553Z","repository":{"id":58761855,"uuid":"339928312","full_name":"Blobfolio/brunch","owner":"Blobfolio","description":"Brunch is a very simple Rust micro-benchmark runner.","archived":false,"fork":false,"pushed_at":"2025-02-25T21:34:49.000Z","size":243,"stargazers_count":28,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-10T00:18:33.324Z","etag":null,"topics":["benchmarking","rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"wtfpl","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Blobfolio.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"Blobfolio"}},"created_at":"2021-02-18T03:46:33.000Z","updated_at":"2025-04-06T08:19:10.000Z","dependencies_parsed_at":"2024-11-07T23:32:13.173Z","dependency_job_id":"2b17c23d-bcae-4d1f-8402-823f7755e3c0","html_url":"https://github.com/Blobfolio/brunch","commit_stats":{"total_commits":144,"total_committers":1,"mean_commits":144.0,"dds":0.0,"last_synced_commit":"71e949828b568083e2bb38620bf8349625e31a25"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blobfolio%2Fbrunch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blobfolio%2Fbrunch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blobfolio%2Fbrunch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Blobfolio%2Fbrunch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Blobfolio","download_url":"https://codeload.github.com/Blobfolio/brunch/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248131318,"owners_count":21052820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["benchmarking","rust"],"created_at":"2024-11-07T23:30:52.642Z","updated_at":"2025-04-10T00:18:38.530Z","avatar_url":"https://github.com/Blobfolio.png","language":"Rust","funding_links":["https://github.com/sponsors/Blobfolio"],"categories":[],"sub_categories":[],"readme":"# Brunch\n\n[![docs.rs](https://img.shields.io/docsrs/brunch.svg?style=flat-square\u0026label=docs.rs)](https://docs.rs/brunch/)\n[![changelog](https://img.shields.io/crates/v/brunch.svg?style=flat-square\u0026label=changelog\u0026color=9b59b6)](https://github.com/Blobfolio/brunch/blob/master/CHANGELOG.md)\u003cbr\u003e\n[![crates.io](https://img.shields.io/crates/v/brunch.svg?style=flat-square\u0026label=crates.io)](https://crates.io/crates/brunch)\n[![ci](https://img.shields.io/github/actions/workflow/status/Blobfolio/brunch/ci.yaml?style=flat-square\u0026label=ci)](https://github.com/Blobfolio/brunch/actions)\n[![deps.rs](https://deps.rs/repo/github/blobfolio/brunch/status.svg?style=flat-square\u0026label=deps.rs)](https://deps.rs/repo/github/blobfolio/brunch)\u003cbr\u003e\n[![license](https://img.shields.io/badge/license-wtfpl-ff1493?style=flat-square)](https://en.wikipedia.org/wiki/WTFPL)\n[![contributions welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\u0026label=contributions)](https://github.com/Blobfolio/brunch/issues)\n\n\n\n`Brunch` is a very simple Rust micro-benchmark runner inspired by [`easybench`](https://crates.io/crates/easybench). It has roughly a million times fewer dependencies than [`criterion`](https://crates.io/crates/criterion), does not require nightly, and maintains a (single) \"last run\" state for each benchmark, allowing it to show relative changes from run-to-run.\n\n(The formatting is also quite pretty.)\n\nAs with all Rust benchmarking, there are a lot of caveats, and results might be artificially fast or slow. For best results:\n* Build optimized;\n* Collect lots of samples;\n* Repeat identical runs to get a feel for the natural variation;\n\n`Brunch` cannot measure time below the level of a nanosecond, so if you're trying to benchmark methods that are _really_ fast, you may need to wrap them in a method that runs through several iterations at once. For example:\n\n```rust\nuse brunch::Bench;\n\n///# Generate Strings to Test.\nfn string_seeds() -\u003e Vec\u003cString\u003e {\n    (0..10_000_usize).into_iter()\n        .map(|i| \"x\".repeat(i))\n        .collect()\n}\n\n///# Generate Strings to Test.\nfn byte_seeds() -\u003e Vec\u003cVec\u003cu8\u003e\u003e {\n    (0..10_000_usize).into_iter()\n        .map(|i| \"x\".repeat(i).into_bytes())\n        .collect()\n}\n\nbrunch::benches!(\n    Bench::new(\"String::len(_)\")\n        .run_seeded_with(string_seeds, |vals| {\n            let mut len: usize = 0;\n            for v in vals {\n                len += v.len();\n            }\n            len\n        }),\n    Bench::new(\"Vec::len(_)\")\n        .run_seeded_with(byte_seeds, |vals| {\n            let mut len: usize = 0;\n            for v in vals {\n                len += v.len();\n            }\n            len\n        }),\n);\n```\n\n\n\n## Installation\n\nAdd `brunch` to your `dev-dependencies` in `Cargo.toml`, like:\n\n```yaml\n[dev-dependencies]\nbrunch = \"0.9.*\"\n```\n\nBenchmarks should also be defined in `Cargo.toml`. Just be sure to set `harness = false` for each:\n\n```yaml\n[[bench]]\nname = \"encode\"\nharness = false\n```\n\nThe following optional environmental variables are supported:\n\n| Variable | Value | Description | Default |\n| -------- | ----- | ----------- | ------- |\n| `NO_BRUNCH_HISTORY` | `1` | Disable run-to-run history. | |\n| `BRUNCH_HISTORY` | Path to history file. | Load/save run-to-run history from this specific path. | `std::env::temp_dir()/__brunch.last` |\n\n\n\n## Usage\n\nThe heart of `Brunch` is the `Bench` struct, which defines a single benchmark. There isn't much configuration required, but each `Bench` has the following:\n\n| Data | Description | Default |\n| ---- | ----------- | ------- |\n| Name | A unique identifier. This is arbitrary, but works best as a string representation of the method itself, like `foo::bar(10)` | |\n| Samples | The number of samples to collect. | 2500 |\n| Timeout | A cutoff time to keep it from running forever. | 10 seconds |\n| Method | A method to run over and over again! | |\n\nThe struct uses builder-style methods to allow everything to be set in a single chain. You always need to start with `Bench::new` and end with one of the runner methods — `Bench::run`, `Bench::run_seeded`, or `Bench::run_seeded_with`. If you want to change the sample or timeout limits, you can add `Bench::with_samples` or `Bench::with_timeout` in between.\n\nThere is also a special `Bench::spacer` method that can be used to inject a linebreak into the results. See below for an example.\n\n### Examples\n\nThe `benches!` macro is the easiest way to run `Brunch` benchmarks.\n\nSimply pass a comma-separated list of all the `Bench` objects you want to run, and it will handle the setup, running, tabulation, and give you a nice summary at the end.\n\nBy default, this macro will generate the `main()` entrypoint too, but you can suppress this by adding \"inline:\" as the first argument.\n\nAnyhoo, the default usage would look something like the following:\n\n```rust\nuse brunch::{Bench, benches};\n\n// Example benchmark adding 2+2.\nfn callback() -\u003e Option\u003cusize\u003e { 2_usize.checked_add(2) }\n\n// Example benchmark multiplying 2x2.\nfn callback2() -\u003e Option\u003cusize\u003e { 2_usize.checked_mul(2) }\n\n// Let the macro handle everything for you.\nbenches!(\n    Bench::new(\"usize::checked_add(2)\")\n        .run(callback),\n\n    Bench::new(\"usize::checked_mul(2)\")\n        .run(callback2),\n);\n```\n\nWhen declaring your own main entrypoint, you need to add \"inline:\" as the first argument. The list of `Bench` instances follow as usual after that.\n\n```rust\nuse brunch::{Bench, benches};\n\n/// # Custom Main.\nfn main() {\n    // A typical use case for the \"inline\" variant would be to declare\n    // an owned variable for a benchmark that needs to return a reference\n    // (to e.g. keep Rust from complaining about lifetimes).\n    let v = vec![0_u8, 1, 2, 3, 4, 5];\n\n    // The macro call goes here!\n    benches!(\n        inline:\n\n        Bench::new(\"vec::as_slice()\").run(|| v.as_slice()),\n    );\n\n    // You can also do other stuff afterwards if you want.\n    eprintln!(\"Done!\");\n}\n```\n\nFor even more control over the flow, skip the macro and just use `Benches` directly.\n\n\n\n## Interpreting Results\n\nIf you run the example benchmark for this crate, you should see a summary like the following:\n\n```text\nMethod                         Mean    Change        Samples\n------------------------------------------------------------\nfibonacci_recursive(30)     2.22 ms    +1.02%    2,408/2,500\nfibonacci_loop(30)         56.17 ns       ---    2,499/2,500\n```\n\nThe _Method_ column speaks for itself, but the numbers deserve a little explanation:\n\n| Column | Description |\n| ------ | ----------- |\n| Mean | The adjusted, average execution time for a _single_ run, scaled to the most appropriate time unit to keep the output tidy. |\n| Change | The relative difference between this run and the last run, if more than two standard deviations. |\n| Samples | The number of valid/total samples, the difference being outliers (5th and 95th quantiles) excluded from consideration. |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblobfolio%2Fbrunch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fblobfolio%2Fbrunch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fblobfolio%2Fbrunch/lists"}