> ## Documentation Index
> Fetch the complete documentation index at: https://codspeed.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Benchmark Rust with divan?

> Learn how to measure the performance of your Rust code by writing and running benchmarks locally and continuously in CI to catch regressions.

export const CIWorkflow = ({minimal = false, enableWorkflowDispatch = true, runsOn = "ubuntu-latest", highlight = [], mode, modes, submodules = false, preSteps = [], buildSteps = ["# ...", "# Setup your environment here:", "#  - Configure your Python/Rust/Node version", "#  - Install your dependencies", "#  - Build your benchmarks (if using a compiled language)", "# ..."], benchmarkCommand = ["<Insert your benchmark command here>"], jobName = "Run benchmarks", env = {}}) => {
  const modeList = modes || (mode ? [mode] : undefined);
  if (!modeList || modeList.length === 0) {
    throw new Error("mode or modes is required");
  }
  const indent = (lines, depth) => {
    const reindentedLines = lines.map(l => l.length === 0 ? l : (" ").repeat(depth) + l);
    return reindentedLines.join("\n");
  };
  const workflowDispatchSection = enableWorkflowDispatch ? "  # `workflow_dispatch` allows CodSpeed to trigger backtest\n" + "  # performance analysis in order to generate initial data.\n" + "  workflow_dispatch:\n" : "";
  let yaml = "";
  if (!minimal) {
    yaml += `
name: CodSpeed Benchmarks

on:
  push:
    branches:
      - "main" # or "master"
  pull_request:
`;
    yaml += workflowDispatchSection;
  }
  yaml += `
jobs:
  benchmarks:
    name: ${jobName}
    runs-on: ${runsOn}`;
  if (!minimal) {
    yaml += `
    permissions: # optional for public repositories
      contents: read # required for actions/checkout
      id-token: write # required for OIDC authentication with CodSpeed`;
  }
  if (preSteps.length > 0) yaml += "\n" + indent(preSteps, 4);
  yaml += `
    steps:
      - uses: actions/checkout@v5`;
  if (submodules) {
    const value = typeof submodules === "string" ? submodules : "true";
    yaml += `\n        with:\n          submodules: ${value}`;
  }
  yaml += "\n" + indent(buildSteps, 6);
  const modeValue = modeList.join(",");
  yaml += `
      - name: Run the benchmarks
        uses: CodSpeedHQ/action@v4
        with:
          mode: ${modeValue}`;
  if (benchmarkCommand.length > 0) {
    const indentedBenchCommand = benchmarkCommand.length > 1 ? benchmarkCommand[0] + "\n" + indent(benchmarkCommand.slice(1), 12) : benchmarkCommand;
    const runLine = indent(["run: "], 10) + indentedBenchCommand;
    yaml += `\n${runLine}`;
  }
  const envEntries = Object.entries(env);
  if (envEntries.length > 0) {
    const envLines = ["env:", ...envEntries.map(([k, v]) => `  ${k}: ${v}`)];
    yaml += "\n" + indent(envLines, 8);
  }
  return <CodeBlock language="yaml" highlight={JSON.stringify(highlight)} {...minimal || ({
    filename: ".github/workflows/codspeed.yml",
    icon: "github"
  })}>
      {yaml}
    </CodeBlock>;
};

export const RustIcon = props => <svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" viewBox="0 0 46 46" width={46} height={46} fill="none" {...props}>
    <path fill="#E4E4E7" d="m44.98 22.453-1.87-1.158a26.12 26.12 0 0 0-.053-.545l1.607-1.5a.64.64 0 0 0-.213-1.074l-2.056-.769a23.292 23.292 0 0 0-.16-.53l1.282-1.782a.643.643 0 0 0-.418-1.013l-2.169-.352a16.396 16.396 0 0 0-.26-.487l.91-2a.641.641 0 0 0-.608-.91l-2.2.077c-.113-.142-.23-.283-.347-.421l.505-2.144a.642.642 0 0 0-.774-.774l-2.143.504c-.139-.117-.278-.232-.422-.348l.077-2.198a.64.64 0 0 0-.91-.609l-2 .911c-.161-.089-.324-.176-.487-.262l-.352-2.167a.644.644 0 0 0-1.012-.42l-1.783 1.282a19.93 19.93 0 0 0-.529-.16l-.769-2.056a.642.642 0 0 0-1.074-.214l-1.5 1.61c-.18-.02-.362-.039-.544-.053l-1.16-1.872a.644.644 0 0 0-1.094 0l-1.159 1.872c-.18.014-.363.033-.544.053l-1.5-1.609a.646.646 0 0 0-1.074.213l-.77 2.056c-.177.052-.354.106-.529.162l-1.782-1.283a.64.64 0 0 0-1.013.418l-.354 2.168c-.163.085-.324.173-.487.262l-1.998-.91a.64.64 0 0 0-.91.607l.076 2.2c-.142.115-.283.23-.422.349L7.846 7.07a.646.646 0 0 0-.776.774l.506 2.144c-.119.14-.234.278-.349.42l-2.199-.076a.644.644 0 0 0-.609.91l.911 2.001c-.087.16-.175.322-.26.488l-2.167.35a.645.645 0 0 0-.42 1.013l1.282 1.782c-.056.175-.109.352-.16.529l-2.056.77a.643.643 0 0 0-.213 1.074l1.608 1.5c-.02.181-.039.362-.054.545l-1.87 1.158a.644.644 0 0 0 0 1.095l1.87 1.158c.015.182.034.365.054.546l-1.608 1.5a.644.644 0 0 0 .213 1.074l2.056.769c.051.178.106.355.161.529l-1.283 1.783a.644.644 0 0 0 .42 1.01l2.167.353c.085.165.171.328.262.488l-.913 1.998a.643.643 0 0 0 .61.911l2.198-.076c.115.143.23.283.349.422l-.506 2.143a.64.64 0 0 0 .776.773l2.142-.504c.14.119.28.233.422.347l-.076 2.2a.641.641 0 0 0 .91.608l2-.91c.16.088.322.176.487.261l.352 2.166a.64.64 0 0 0 1.013.42l1.781-1.284c.176.056.353.112.53.162l.768 2.056a.641.641 0 0 0 1.074.213l1.502-1.609c.18.02.362.04.544.056l1.16 1.87a.645.645 0 0 0 1.094 0l1.157-1.872c.183-.015.365-.033.546-.054l1.5 1.609a.641.641 0 0 0 1.073-.213l.77-2.056c.177-.05.354-.106.53-.162l1.782 1.285a.645.645 0 0 0 1.012-.421l.353-2.167c.163-.084.325-.173.486-.26l2 .91a.64.64 0 0 0 .91-.609l-.077-2.2c.142-.112.283-.227.422-.346l2.143.505a.641.641 0 0 0 .774-.774l-.504-2.143c.117-.14.232-.279.347-.422l2.2.076a.64.64 0 0 0 .608-.91l-.911-2c.089-.16.175-.322.26-.487l2.167-.352a.64.64 0 0 0 .42-1.011l-1.282-1.783c.056-.176.11-.352.16-.53l2.056-.768a.642.642 0 0 0 .213-1.074l-1.608-1.5c.02-.181.037-.364.054-.546l1.87-1.158a.644.644 0 0 0 0-1.095ZM32.459 37.975a1.326 1.326 0 0 1 .555-2.592 1.326 1.326 0 0 1-.555 2.592Zm-.637-4.3a1.205 1.205 0 0 0-1.432.928l-.664 3.1A16.226 16.226 0 0 1 23 39.154c-2.451 0-4.777-.544-6.865-1.515l-.665-3.099a1.206 1.206 0 0 0-1.431-.929l-2.737.588a16.277 16.277 0 0 1-1.416-1.667h13.317c.15 0 .251-.028.251-.165v-4.71c0-.138-.1-.165-.25-.165h-3.895v-2.987h4.212c.384 0 2.056.11 2.59 2.246.168.657.535 2.794.786 3.478.251.768 1.27 2.301 2.357 2.301h6.635c.081 0 .162-.008.241-.023-.46.625-.965 1.217-1.508 1.769l-2.8-.6h-.001ZM13.403 37.91a1.326 1.326 0 0 1-.555-2.592 1.325 1.325 0 0 1 1.39 2.018 1.327 1.327 0 0 1-.836.575l.001-.001ZM8.351 17.425A1.323 1.323 0 1 1 5.933 18.5a1.323 1.323 0 0 1 2.418-1.074Zm-1.553 3.682 2.852-1.268a1.21 1.21 0 0 0 .612-1.595l-.586-1.327h2.31v10.41h-4.66a16.315 16.315 0 0 1-.528-6.22Zm12.512-1.012v-3.068h5.5c.284 0 2.005.329 2.005 1.616 0 1.068-1.32 1.452-2.407 1.452H19.31Zm19.986 2.762c0 .407-.015.81-.046 1.21H37.58c-.167 0-.234.11-.234.274v.767c0 1.808-1.02 2.201-1.912 2.301-.85.095-1.794-.356-1.91-.877-.501-2.82-1.337-3.424-2.658-4.466 1.639-1.039 3.343-2.574 3.343-4.628 0-2.219-1.52-3.616-2.557-4.301-1.454-.959-3.064-1.15-3.499-1.15h-17.29a16.274 16.274 0 0 1 9.116-5.146l2.037 2.138a1.205 1.205 0 0 0 1.707.04l2.281-2.182a16.311 16.311 0 0 1 11.155 7.945l-1.561 3.526a1.21 1.21 0 0 0 .613 1.595l3.007 1.335c.052.533.08 1.072.08 1.62ZM22.016 5.022a1.324 1.324 0 1 1 1.828 1.918 1.32 1.32 0 0 1-1.87-.045 1.327 1.327 0 0 1 .043-1.873ZM37.51 17.49a1.324 1.324 0 1 1 2.42 1.076 1.324 1.324 0 0 1-2.42-1.076Z" />
  </svg>;

## Why divan?

This guide uses [`divan`](https://docs.rs/divan/latest/divan/) because it
strikes the best balance between power and simplicity:

* **Extensive features** for both simple and complex benchmarking scenarios.
* **Intuitive API** that's approachable but powerful when needed.
* **Works on stable Rust** without requiring nightly features.

`divan` also works seamlessly with parametrization, type generics, and dynamic
input generation. You can even benchmark across different types to compare their
performance characteristics.

<Info>
  Rust has several benchmarking frameworks to choose from:
  [`divan`](https://crates.io/crates/divan),
  [`criterion.rs`](https://crates.io/crates/criterion), and [`libtest
      (bencher)`](https://crates.io/crates/bencher). This guide uses `divan` for its
  simplicity and powerful features.
</Info>

## Your First Benchmark

Let's start by creating a simple benchmark for a recursive Fibonacci function.

### Installation

First, add `divan` to your project's dev dependencies:

```bash icon="square-terminal" theme={null}
cargo add --dev divan
```

### Writing the Benchmark

Create a new file in `benches/fibonacci.rs`:

```rust benches/fibonacci.rs icon="rust" theme={null}
fn main() {
    // Run registered benchmarks.
    divan::main();
}

// Define the function we want to benchmark
fn fibonacci(n: u64) -> u64 {
    if n <= 1 {
        1
    } else {
        fibonacci(n - 2) + fibonacci(n - 1)
    }
}

// Register a simple benchmark
#[divan::bench]
fn fib_bench() -> u64 {
    fibonacci(divan::black_box(10))
}
```

A few things to note:

* `divan::main()` discovers and runs all benchmarks in the file.
* `#[divan::bench]` marks a function as a benchmark.
* `divan::black_box()` prevents the compiler from optimizing away our function
  call.

### Configuration

Add the benchmark target to your `Cargo.toml`:

```toml Cargo.toml theme={null}
[[bench]]
name = "fibonacci"
harness = false
```

The `harness = false` setting tells Cargo to use `divan`'s benchmark runner
instead of the default one.

<Warning>
  This step is mandatory for `divan` benchmarks to work correctly. Without it, the
  benchmarks will not run at all.

  In the rest of this guide, we'll assume you've added this configuration for each
  of the shown benchmark files.
</Warning>

### Running the Benchmark

Now run your benchmark:

```bash icon="square-terminal" theme={null}
cargo bench
```

You should see output like this:

```shellsession title=terminal icon="square-terminal" theme={null}
fibonacci     fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ fib_bench  158.5 ns      │ 165 ns        │ 159.8 ns      │ 160.2 ns      │ 100     │ 3200
```

The fastest measured execution of `fibonacci(10)` is 158.5 nanoseconds.

## Benchmarking with Arguments

So far, we've only tested our function with a single input value (10). But what
if we want to see how performance changes with different input sizes? This is
where the `args` parameter comes in.

Let's update our benchmark to test multiple input sizes:

```rust benches/fibonacci.rs icon="rust" theme={null}
// Register a benchmark with multiple input sizes
#[divan::bench(args = [1, 2, 4, 8, 16, 32])]
fn fib_bench(n: u64) -> u64 {
    fibonacci(divan::black_box(n))
}
```

Now when you run `cargo bench`, you'll see results for each input:

```shellsession title=terminal icon="square-terminal" theme={null}
fibonacci     fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ fib_bench                │               │               │               │         │
   ├─ 1       1.241 ns      │ 1.282 ns      │ 1.251 ns      │ 1.256 ns      │ 100     │ 409600
   ├─ 2       3.438 ns      │ 4.069 ns      │ 3.459 ns      │ 3.479 ns      │ 100     │ 204800
   ├─ 4       7.527 ns      │ 9.358 ns      │ 7.568 ns      │ 7.633 ns      │ 100     │ 102400
   ├─ 8       57.61 ns      │ 82.68 ns      │ 58.59 ns      │ 59.21 ns      │ 100     │ 12800
   ├─ 16      2.874 µs      │ 3.312 µs      │ 2.916 µs      │ 2.936 µs      │ 100     │ 200
   ╰─ 32      6.28 ms       │ 6.984 ms      │ 6.397 ms      │ 6.43 ms       │ 100     │ 100
```

Looking at our Fibonacci results, we can see the exponential growth:

* **Nanoseconds (ns)**: For small inputs (1-4), the function is incredibly fast.
* **Microseconds (µs)**: At n=16, we're in the microsecond range (1,000x
  slower).
* **Milliseconds (ms)**: At n=32, we've reached milliseconds (1,000,000x slower
  than n=1).

This exponential growth tells us we should probably use a different algorithm
for larger inputs. This is the O(2^n) complexity of naive recursive Fibonacci in
action.

## Benchmarking only what matters

Sometimes, you want to exclude setup time from your benchmarks. For example, if
you're benchmarking a search function that operates on a large dataset, you
don't want to include the time it takes to create that dataset in every
iteration.

Here's how to do that using
[`divan`'s `Bencher`](https://docs.rs/divan/latest/divan/struct.Bencher.html):

```rust benches/vector_search.rs icon="rust" theme={null}
fn main() {
    divan::main();
}

#[divan::bench(args = [100, 1000, 10000])]
fn search_vector(bencher: divan::Bencher, size: usize) {
    // Setup: create a vector with test data
    // This runs once before all iterations
    let data: Vec<i32> = (0..size as i32).collect();
    let target = size as i32 / 2;

    bencher.bench_local(|| {
        // Only this part is measured
        data.iter().find(|&&x| x == target)
    });
}
```

The setup code (creating the vector) runs once before benchmarking starts, and
only the search operation inside `bench_local` is measured. This is perfect when
you can reuse the same input data across all iterations.

## Advanced Techniques

Now that you understand the basics, let's explore `divan`'s advanced features
that make it particularly powerful.

### Type Generics

You can benchmark the same operation across different types to compare their
performance:

```rust benches/types.rs icon="rust" theme={null}
fn main() {
    divan::main();
}

#[divan::bench(types = [&str, String])]
fn from_str<'a, T>() -> T
where
    T: From<&'a str>,
{
    divan::black_box("hello world").into()
}
```

This benchmarks the conversion from `&str` to both `&str` (no-op) and `String`
(allocation), showing the performance difference:

```shellsession title=terminal icon="square-terminal" theme={null}
types         fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ from_str                 │               │               │               │         │
   ├─ &str    0.6 ns        │ 3.357 ns      │ 0.61 ns       │ 0.664 ns      │ 100     │ 819200
   ╰─ String  15.96 ns      │ 131.8 ns      │ 16.61 ns      │ 18.13 ns      │ 100     │ 6400
```

**Use case**: Compare `Vec<T>` vs. `Box<[T]>`, `HashMap` vs. `BTreeMap`, or any
types that implement the same trait.

### Dynamic Input Generation

Sometimes you need fresh input data for each benchmark iteration, for example,
when benchmarking operations that consume or modify their input. You can use
[`with_inputs`](https://docs.rs/divan/latest/divan/struct.Bencher.html#method.with_inputs)
to generate new data for each iteration without that generation time being
measured.

Let's benchmark a JSON parsing function that needs a fresh string each time. For
this example, we'll use [`serde_json`](https://crates.io/crates/serde_json),
Rust's most popular JSON library:

```bash icon="square-terminal" theme={null}
cargo add --dev serde_json
```

```rust benches/json_parsing.rs icon="rust" theme={null}
fn main() {
    divan::main();
}

// Expensive function to generate test data
fn generate_large_json(size: usize) -> String {
    let items: Vec<_> = (0..size)
        .map(|i| format!(r#"{{"id":{},"name":"item_{}","value":{}}}"#, i, i, i * 10))
        .collect();
    format!("[{}]", items.join(","))
}

#[divan::bench(args = [10, 100, 1000])]
fn parse_json(bencher: divan::Bencher, size: usize) {
    bencher
        .with_inputs(|| {
            // Generate test JSON data for each iteration.
            // This time is NOT measured.
            generate_large_json(size)
        })
        .bench_values(|json_string| {
            // This is what we're actually benchmarking:
            // parsing the JSON string.
            serde_json::from_str::<serde_json::Value>(&json_string)
        });
}
```

The `with_inputs` closure runs before each benchmark iteration, but its
execution time is excluded from the measurements. This ensures you're only
measuring the parsing performance, not the data generation.

**When to use this**:

* Generating random or large test data.
* Loading files or fixtures.
* Creating complex data structures.
* Any expensive setup that shouldn't affect your measurements.

**Important**: Use `with_inputs` when the input needs to be fresh for each
iteration. For inputs that can be reused across iterations, create them once
before calling `bencher`.

### Benchmarking Async Functions

To benchmark asynchronous functions, let's use the popular
[`tokio`](https://crates.io/crates/tokio) runtime. First, add `tokio` to your
dev dependencies:

```bash icon="square-terminal" theme={null}
cargo add --dev tokio --features time,rt-multi-thread
```

To benchmark async functions, we will create a Tokio runtime inside the
benchmark and use it to execute the async code. We will use `bench_local` to
ensure only the async function execution time is measured, excluding the runtime
setup time.

```rust benches/async.rs icon="rust" theme={null}
use tokio::runtime::Runtime;
use tokio::time::{Duration, sleep};

fn main() {
    divan::main();
}

#[divan::bench]
fn async_sleep_benchmark(bencher: divan::Bencher) {
    let rt = Runtime::new().unwrap();

    bencher.bench_local(|| {
        rt.block_on(async {
            sleep(Duration::from_millis(100)).await; // simulates async work for 100ms
        });
    });
}
```

Here is the output when you run the benchmark:

```shellsession title=terminal icon="square-terminal" theme={null}
async                     fastest       │ slowest       │ median        │ mean          │ samples │ iters
╰─ async_sleep_benchmark  100.8 ms      │ 114.1 ms      │ 104.2 ms      │ 104 ms        │ 100     │ 100
```

The results are close to the expected 100ms sleep time, but there is some
overhead. This is because we are also measuring `block_on` and the context
switching involved in async execution. Async benchmarks are planned to be
[supported natively in future versions of `divan`](https://github.com/nvzqz/divan/issues/39).

<Tip>
  Since asynchronous functions most likely involve I/O operations, their execution
  time can vary significantly based on external factors like network latency or
  disk speed. When benchmarking async code, consider running more iterations or
  rounds to obtain reliable measurements.

  If you are using CodSpeed in your CI to run your benchmarks, be sure to use the
  [Walltime instrument](/instruments/walltime) to get accurate timing for async
  operations.
</Tip>

## Best Practices

### Ensure code is not optimized out

The Rust compiler is incredibly smart and might optimize away your benchmark if
the result isn't used. Here's how to prevent this:

```rust icon="rust" theme={null}
// ❌ BAD: Compiler might optimize this away
#[divan::bench]
fn bad_bench() {
    fibonacci(10); // Result not used
}

// ✅ BEST: Return the value from your benchmark
#[divan::bench]
fn good_bench() -> u64 {
    fibonacci(divan::black_box(10))
}

// ✅ ALTERNATIVE: Use black_box on the output
#[divan::bench]
fn alternative_bench() {
    divan::black_box(fibonacci(divan::black_box(10)));
}
```

**The go-to solution is returning the value** from your benchmark function. This
automatically prevents the compiler from optimizing away the computation and
also avoids measuring the time to drop the result (which can be significant for
types like `String` or `Vec`).

Use `divan::black_box` on inputs to prevent the compiler from making assumptions
about known values at compile time:

```rust icon="rust" theme={null}
// Prevent optimization based on known input values
#[divan::bench(args = [1, 10, 100])]
fn benchmark_with_args(n: u64) -> u64 {
    // black_box the input to prevent compile-time optimizations
    fibonacci(divan::black_box(n))
}
```

<Tip>
  **Return values when possible**, use `black_box` on inputs to prevent
  compile-time optimizations. Only use `black_box` on outputs when you can't
  return the value.
</Tip>

<Info>
  Learn more about preventing compiler optimizations in the [divan `black_box`
  documentation](https://docs.rs/divan/latest/divan/fn.black_box.html).
</Info>

### Benchmark Your Crate Functions

In real-world projects, you'll want to benchmark functions from your own crate,
not functions defined directly in the benchmark file. Here's how to set up
benchmarks for a typical algorithms library with synthetic data generation.

Let's say you have a sorting library with this function in `src/lib.rs`:

```rust src/lib.rs icon="rust" theme={null}
pub fn bubble_sort(mut arr: Vec<i32>) -> Vec<i32> {
    let n = arr.len();
    for i in 0..n {
        for j in 0..n - 1 - i {
            if arr[j] > arr[j + 1] {
                arr.swap(j, j + 1);
            }
        }
    }
    arr
}
```

Here is what the benchmark file `benches/sorting.rs` would look like to
benchmark this function with synthetic data:

```rust benches/sorting.rs icon="rust" theme={null}
use my_lib::bubble_sort; // replace `my_lib` with your crate name

fn main() {
    divan::main();
}

// Generate synthetic test data
fn generate_random_vec(size: usize) -> Vec<i32> {
    use std::collections::hash_map::DefaultHasher;
    use std::hash::{Hash, Hasher};

    (0..size)
        .map(|i| {
            let mut hasher = DefaultHasher::new();
            i.hash(&mut hasher);
            (hasher.finish() % 10000) as i32
        })
        .collect()
}

#[divan::bench(args = [100, 1000, 10_000])]
fn bench_bubble_sort(bencher: divan::Bencher, size: usize) {
    bencher
        .with_inputs(|| generate_random_vec(size))
        .bench_values(|data| bubble_sort(data));
}
```

With multiple benchmark files, your project structure will look like this:

```shellsession title=terminal icon="square-terminal" theme={null}
my_lib/
├── Cargo.toml
├── src/
│   ├── searching.rs
│   ├── sorting.rs
│   └── lib.rs
└── benches/
    ├── searching.rs
    └── sorting.rs
```

<Tip>
  To only run a specific benchmark file, you can pass its name to `cargo bench`:

  ```bash icon="square-terminal" theme={null}
  cargo bench --bench sorting # only runs benchmarks in benches/sorting.rs
  ```
</Tip>

### Workspace with Multiple Crates

If you're working in a workspace with multiple crates, your setup can look like
this:

```shellsession title=terminal icon="square-terminal" theme={null}
my_workspace/
├── Cargo.toml
├── crate_a/
│   ├── Cargo.toml
│   ├── src
│   │   ├── searching.rs
│   │   ├── sorting.rs
│   │   └── lib.rs
│   └── benches/
│       ├── searching.rs
│       └── sorting.rs
└── crate_b/
    ├── Cargo.toml
    ├── src/
    │   └── lib.rs
    └── benches/
        └── processing.rs
```

In that case, you can have a single reference to `divan` in the root
`Cargo.toml` and each crate's `Cargo.toml` can refer to it as a workspace
member:

```toml Cargo.toml theme={null}
[workspace]
members = ["crate_a", "crate_b"]

[dev-dependencies]
divan = "0.1.21"
```

And each crate's `Cargo.toml` can look like this:

```toml Cargo.toml theme={null}
[package]
name = "crate_a"
version = "0.1.0"
edition = "2021"

[[bench]]
name = "sorting"
harness = false

[dev-dependencies]
divan = { workspace = true } # use the workspace version
```

<Tip>
  You can then use the `-p` flag to run the benchmarks for specific crates:

  ```bash icon="square-terminal" theme={null}
  cargo bench # will run benchmarks in all workspace crates

  cargo bench -p crate_a # will only run benchmarks in crate_a
  cargo bench -p crate_b # will only run benchmarks in crate_b

  cargo bench -p crate_a --bench sorting # only runs benchmarks in crate_a's sorting.rs
  ```
</Tip>

## Running Benchmarks Continuously with CodSpeed

So far, you've been running benchmarks locally. But local benchmarking has
limitations:

* **Inconsistent hardware**: Different developers get different results
* **Manual process**: Easy to forget to run benchmarks before merging
* **No historical tracking**: Hard to spot gradual performance degradation
* **No PR context**: Can't see performance impact during code review

This is where **CodSpeed** comes in. It runs your benchmarks automatically in CI
and provides:

* Automated performance regression detection in PRs
* Consistent metrics with reliable measurements across all runs
* Historical tracking to see performance over time with detailed charts
* Flamegraph profiles to see exactly what changed in your code's execution

<Info>
  CodSpeed works with all three Rust benchmarking frameworks: `divan`,
  `criterion.rs`, and `bencher`. If you're already using `criterion.rs` or
  `bencher`, check out their respective [CodSpeed integration
  guides](/benchmarks/rust).
</Info>

<Tip>
  For the full CodSpeed integration reference, see [Writing Benchmarks with
  divan](/benchmarks/rust/divan).
</Tip>

### How to set up CodSpeed with divan

Here's how to integrate CodSpeed with your `divan` benchmarks:

<Steps>
  <Step title="Install cargo-codspeed">
    First, install the `cargo-codspeed` CLI tool locally to test:

    ```bash icon="square-terminal" theme={null}
    cargo install cargo-codspeed --locked
    ```
  </Step>

  <Step title="Switch to CodSpeed Compatibility Layer">
    CodSpeed provides a drop-in replacement for divan that adds instrumentation for
    profiling. Replace your `divan` dependency with the CodSpeed compatibility
    layer:

    ```bash icon="square-terminal" theme={null}
    cargo add --dev codspeed-divan-compat --rename divan
    ```

    This command updates your `Cargo.toml` to use the CodSpeed compatibility layer
    while keeping the name `divan`, so you don't need to change any of your
    benchmark code:

    ```toml Cargo.toml theme={null}
    [dev-dependencies]
    divan = { package = "codspeed-divan-compat", version = "*" }
    ```

    <Tip>
      The compatibility layer doesn't change your benchmark behavior when running
      `cargo bench` locally, it only adds instrumentation when running in a CodSpeed
      environment.
    </Tip>
  </Step>

  <Step title="Test Locally">
    First, build your benchmarks with the CodSpeed instrumentation harness:

    ```shellsession title=terminal icon="square-terminal" theme={null}
    $ cargo codspeed build
    [cargo-codspeed] Measurement mode: Instrumentation

       Compiling libc v0.2.177
       ... # other dependencies
        Finished `bench` profile [optimized] target(s) in 19.47s
    Built benchmark `fibonacci` in package `docs-guides`
    Built benchmark `vector_search` in package `docs-guides`
    Built benchmark `types` in package `docs-guides`
    Built benchmark `json_parsing` in package `docs-guides`
    Built 4 benchmark suite(s)
    ```

    This compiles your benchmarks with CodSpeed's instrumentation enabled, which
    will capture detailed profiling information during execution.

    Then run the benchmarks to verify everything works:

    ```shellsession title=terminal icon="square-terminal" theme={null}
    $ cargo codspeed run
    [cargo-codspeed] Measurement mode: Instrumentation

    Collected 4 benchmark suite(s) to run
    Running docs-guides json_parsing
    json_parsing
    ╰─ parse_json
       ├─ 10
       ├─ 100
       ╰─ 1000

    Done running json_parsing

    ... # other benchmark outputs

    Running docs-guides vector_search
    vector_search
    ╰─ search_vector
       ├─ 100
       ├─ 1000
       ╰─ 10000

    Done running vector_search
    Finished running 4 benchmark suite(s)
    ```

    <Info>
      Notice there are no performance measurements (no timing numbers) in the local
      output. Here, we verify your benchmarks compile and execute correctly.

      CodSpeed only captures actual performance data when running in CI or locally
      with the `codspeed` CLI.
      [Learn more on how to use the `codspeed` CLI locally](https://github.com/CodSpeedHQ/codspeed#usage).
      At the moment, local runs are only supported on Ubuntu and Debian.
    </Info>
  </Step>

  <Step title="Set Up GitHub Actions">
    Create a workflow file to run benchmarks on every push and pull request:

    <CIWorkflow
      mode="simulation"
      buildSteps={[
    "- name: Setup rust toolchain, cache and cargo-codspeed binary",
    "  uses: moonrepo/setup-rust@v1",
    "  with:",
    "    channel: stable",
    "    cache-target: release",
    "    bins: cargo-codspeed",
    "",
    "- name: Build the benchmark target(s)",
    "  run: cargo codspeed build",
  ]}
      benchmarkCommand={["cargo codspeed run"]}
    />
  </Step>

  <Step title="Check the Results">
    Once the workflow runs, your pull requests will receive a performance report
    comment:

    <img src="https://mintcdn.com/codspeed/jKaxX6yy-Kzw1C-0/assets/pr-comment-new-installation.png?fit=max&auto=format&n=jKaxX6yy-Kzw1C-0&q=85&s=4405db6390fe6f80b4f13d5baa2598d1" className="rounded-xl w-full max-w-lg mx-auto" alt="Pull Request Result" width="1744" height="820" data-path="assets/pr-comment-new-installation.png" />

    <img src="https://mintcdn.com/codspeed/jKaxX6yy-Kzw1C-0/assets/pr-status-check-success.png?fit=max&auto=format&n=jKaxX6yy-Kzw1C-0&q=85&s=a74b568e364c0b068623bd31ee869361" className="rounded-xl w-full max-w-md mx-auto" alt="Pull Request Result" width="1408" height="690" data-path="assets/pr-status-check-success.png" />
  </Step>

  <Step title="Access Detailed Reports and Flamegraphs">
    After your benchmarks run in CI, head over to your CodSpeed dashboard to see
    detailed performance reports, historical trends, and flamegraph profiles for
    deeper analysis.

    <Frame caption="Profiling Report on CodSpeed">
      <img src="https://mintcdn.com/codspeed/CInbng288QuXBkrC/features/assets/cover.png?fit=max&auto=format&n=CInbng288QuXBkrC&q=85&s=302d47fea90881b1af8ab6c21148c245" className="p-4 w-full max-w-lg mx-auto" alt="Profiling Report on CodSpeed" width="1171" height="685" data-path="features/assets/cover.png" />
    </Frame>

    <Tip>
      Profiling works out of the box, no extra configuration needed!

      [Learn more about flamegraphs and how to use them to optimize your code](/features/profiling).
    </Tip>
  </Step>
</Steps>

## Next Steps

Check out these resources to continue your Rust benchmarking journey:

<CardGroup cols={2}>
  <Card title="Get Started with CodSpeed" href="https://codspeed.io?flow=get-started" icon="rocket">
    Sign up and start tracking your Rust performance in CI
  </Card>

  <Card title="CodSpeed Rust Benchmarking Docs" href="/benchmarks/rust" icon={<RustIcon />}>
    Explore more Rust benchmarking techniques and integrations
  </Card>

  <Card title="Performance Profiling" href="/features/profiling" icon="fire">
    Learn how to use flamegraphs to optimize your code
  </Card>

  <Card title="divan Documentation" href="https://docs.rs/divan/" icon="book">
    Explore all of divan's features in depth
  </Card>
</CardGroup>
