> ## 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.

# Writing Benchmarks with divan

> Using the divan compatibility layer for CodSpeed

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>;
};

## Installation

<Note>
  For all Rust integrations, you will need
  [the `cargo-codspeed` command](/benchmarks/rust/overview#cargo-codspeed) to
  build and run your CodSpeed benchmarks
</Note>

Install the
[`divan` compatibility layer](https://crates.io/crates/codspeed-divan-compat):

```sh theme={null}
cargo add --dev codspeed-divan-compat --rename divan
```

Or directly change your `Cargo.toml` if you already have `divan` installed:

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

This will install the `codspeed-divan-compat` crate and rename it to `divan` in
your `Cargo.toml`. This way, you can keep your existing imports and the
compatibility layer will take care of the rest.

<Tip>
  The compatibility layer is a passthrough when not using `cargo codspeed`,
  running `cargo bench` will behave exactly as with the default `divan` crate.
</Tip>

<Info>
  If you prefer, you can also install `codspeed-divan-compat` as is and change
  your imports to use this new crate name.
</Info>

## Usage

### Creating benchmarks

As an example, let's follow the example from the
[divan documentation](https://docs.rs/divan/0.1.17/divan/#getting-started): a
benchmark suite for the Fibonacci function:

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

// Register a `fibonacci` function and benchmark it over multiple cases.
#[divan::bench(args = [1, 2, 4, 8, 16, 32])]
fn fibonacci(n: u64) -> u64 {
    if n <= 1 {
        1
    } else {
        fibonacci(n - 2) + fibonacci(n - 1)
    }
}
```

The last step in creating the divan benchmark is to add the new benchmark target
in your `Cargo.toml`:

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

And that's it! You can now run your benchmark suite with CodSpeed

### Testing the benchmarks locally

```shellsession title=terminal icon="square-terminal" theme={null}
$ cargo codspeed build
    Finished release [optimized] target(s) in 0.12s
    Finished built 1 benchmark suite(s)
$ cargo codspeed run
   Collected 1 benchmark suite(s) to run
   Running my_benchmark
NOTICE: codspeed is enabled, but no performance measurement will be made since it's running in an unknown environment.
Checked: benches/my_benchmark.rs::fibo_bench[1]
Checked: benches/my_benchmark.rs::fibo_bench[2]
Checked: benches/my_benchmark.rs::fibo_bench[4]
Checked: benches/my_benchmark.rs::fibo_bench[8]
Checked: benches/my_benchmark.rs::fibo_bench[16]
Checked: benches/my_benchmark.rs::fibo_bench[32]
        Done running my_benchmark
    Finished running 1 benchmark suite(s)
```

Congrats ! 🎉 You can now
[run those benchmark in your CI](#running-the-benchmarks-in-your-ci) to get the
actual performance measurements.

<Info>
  Use `--measurement-mode` / `-m` to select the CodSpeed instrument:

  * **`simulation`** (default): Runs benchmarks once on a
    [simulated CPU](/instruments/cpu) for consistent measurements.
  * **`walltime`**: Measures [wall-clock time](/instruments/walltime) for
    real-world scenarios.
  * **`memory`**: Benchmarks are run once using
    [memory profiling](/instruments/memory) to track heap allocations and memory
    usage.

  See the
  [`cargo-codspeed` reference](/reference/codspeed-rust/cargo-codspeed#the-measurement-mode-flag)
  for more information.
</Info>

### Running the benchmarks in your CI

To generate performance reports, you need to run the benchmarks in your CI. This
allows CodSpeed to automatically run benchmarks and warn you about regressions
during development.

<Tip>
  If you want more details on how to configure the CodSpeed action, you can check
  out the [Continuous Reporting section](/integrations/ci).
</Tip>

Here is an example of a GitHub Actions workflow that runs the benchmarks and
reports the results to CodSpeed on every push to the `main` branch and every
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"]}
/>

### Advanced usage

Divan provides a lot of convenient features to help you write benchmars, below
is a selection that can be useful in CodSpeed benchmarks, but check out the
[divan documentation](https://docs.rs/divan/latest/divan/) for an exhaustive
list of features.

<Card title="How to Benchmark Rust with divan" icon="rust" href="/guides/how-to-benchmark-rust-with-divan" horizontal>
  An in-depth guide to writing divan benchmarks: parameterized benchmarks, type
  generics, dynamic inputs, and CodSpeed CI integration.
</Card>

#### Type generics

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

#### Combining type generics and arguments

```rust benches/types_and_args.rs theme={null}
use std::collections::{BTreeSet, HashSet};

#[divan::bench(
    types = [Vec<i32>, BTreeSet<i32>, HashSet<i32>],
    args = [0, 2, 4, 16, 256, 4096],
)]
fn from_range<T>(n: i32) -> T
where
    T: FromIterator<i32>,
{
    (0..n).collect()
}
```

#### Generating dynamic inputs

Time spent generating inputs is not measured in benchmarks.

```rust benches/with_inputs.rs theme={null}
#[divan::bench]
fn bench(bencher: divan::Bencher) {
    bencher
        .with_inputs(|| {
            // Generate input:
            String::from("...")
        })
        .bench_values(|s| {
            // Use input by-value:
            s + "123"
        });
}
```

## Recipes

### Running benchmarks in parallel CI jobs

With Rust, if you use multiple packages, a first sharding optimization is to
split your benchmarks across these packages.

For example, using Github Actions:

<CIWorkflow
  minimal
  mode="simulation"
  highlight={[5, 6, 7, 8, 9, 19, 24]}
  preSteps={[
"strategy:",
"  matrix:",
"    package:",
"      - my-first-package",
"      - my-second-package",
]}
  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 -p ${{ matrix.package }}",
]}
  benchmarkCommand={["cargo codspeed run # only runs the built benchmarks"]}
/>

<Tip>
  It is not required to pass a `-p` flag as only the benchmarks built by `cargo
      codspeed build` will be run.
</Tip>

For more information about multiple packages, check
[the cargo-codspeed docs](/benchmarks/rust/).

<Danger>
  **Same benchmark with different variations**

  For now, you cannot run the same benchmarks several times within the same run.
  If the same benchmark is run multiple times, you will receive the following
  comment on your pull request:

  <img src="https://mintcdn.com/codspeed/jKaxX6yy-Kzw1C-0/assets/parallel-benchmarks-variations-warning.png?fit=max&auto=format&n=jKaxX6yy-Kzw1C-0&q=85&s=f5e87d3fecc76f2fe3ce7f302e7f9a93" className="rounded-xl w-full max-w-lg mx-auto" alt="Multiple Benchmark Variations Error Message" width="1832" height="568" data-path="assets/parallel-benchmarks-variations-warning.png" />
</Danger>

Learn more about
[benchmark sharding and how to integrate with your CI provider](/features/sharded-benchmarks).

<Info>
  To combine measurement modes like simulation and memory, check out the
  documentation on [running multiple instruments
  serially](/integrations/ci/github-actions/configuration#running-multiple-instruments-serially).
</Info>
