Cargo can build and execute Rust benchmark targets from the same package manifest that drives builds and tests. Running benchmarks before a performance-sensitive change or release gives the crate an optimized timing pass instead of relying on debug builds or ordinary unit tests.

Benchmark targets usually live under benches, and Cargo builds them with the bench profile. The built-in #[bench] attribute is nightly-only, so stable projects commonly use a custom harness or a benchmarking crate such as Criterion while keeping cargo bench as the entry command.

A small custom harness with harness = false can run on stable Rust without external dependencies. Existing projects that already have benchmark targets can skip the file setup and run cargo bench from the package root.

Steps to run Rust benchmarks with Cargo:

  1. Open the Rust package root that contains Cargo.toml.

    Use cargo bench --manifest-path path/to/Cargo.toml when launching Cargo from another directory.

  2. Create the benchmark directory when the package does not already have one.
    $ mkdir -p benches
  3. Add a benchmark target.
    benches/string_len.rs
    use std::{hint::black_box, time::Instant};
     
    fn main() {
        let input = "simplified-guide-benchmark";
        let rounds = 1_000_000;
        let started = Instant::now();
        let mut total = 0;
     
        for _ in 0..rounds {
            total += black_box(input).len();
        }
     
        black_box(total);
        println!(
            "string_len: {rounds} iterations in {:?} (total={total})",
            started.elapsed()
        );
    }

    std::hint::black_box keeps the optimizer from removing the measured loop in the minimal benchmark.

  4. Register the benchmark as a custom harness in Cargo.toml.
    Cargo.toml
    [[bench]]
    name = "string_len"
    harness = false

    harness = false disables the default libtest benchmark harness, so the benchmark file provides its own main function.

  5. Build the benchmark target without executing it.
    $ cargo bench --no-run --bench string_len
       Compiling demo-lib v0.1.0 (/home/dev/demo-lib)
        Finished `bench` profile [optimized] target(s) in 1.09s
      Executable benches/string_len.rs (target/release/deps/string_len-2ce2528f213845aa)

    Use --no-run when compile errors should be caught before collecting timing output.

  6. Run the selected benchmark target.
    $ cargo bench --bench string_len
        Finished `bench` profile [optimized] target(s) in 0.01s
         Running benches/string_len.rs (target/release/deps/string_len-2ce2528f213845aa)
    string_len: 1000000 iterations in 581.792µs (total=26000000)

    A custom main function controls this output line. A Criterion benchmark or nightly #[bench] target reports in its own harness format.

  7. Run every benchmark target selected by the package manifest.
    $ cargo bench
       Compiling demo-lib v0.1.0 (/home/dev/demo-lib)
        Finished `bench` profile [optimized] target(s) in 0.27s
         Running unittests src/lib.rs (target/release/deps/demo_lib-3c68178e29f1f907)
    
    running 1 test
    test tests::it_works ... ignored
    
    test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
    
         Running benches/string_len.rs (target/release/deps/string_len-2ce2528f213845aa)
    string_len: 1000000 iterations in 398.625µs (total=26000000)

    Options before -- belong to Cargo. Arguments after -- are passed to the benchmark binary or harness.