> ## 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 CLI Commands

> Benchmark any language with the CodSpeed CLI and a configuration file

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

CodSpeed provides [dedicated integrations](/benchmarks/overview) for several
languages, but you do not need one to start benchmarking. The
[CodSpeed CLI](/cli) can benchmark **any executable program**: define the
commands to measure in a `codspeed.yml` configuration file, and run them locally
or in CI.

This approach works for any language or toolchain, even if it does not have a
dedicated integration yet, e.g., Zig, OCaml, or Swift. It is also a good fit for
benchmarking a program end to end, like a CLI tool or a compiler.

<Note>
  Benchmarks defined this way measure a whole command, including process
  startup. If your language has a [dedicated
  integration](/benchmarks/overview), prefer it to benchmark individual
  functions. Otherwise, you can also
  [build a custom harness](#benchmarking-a-subpart-of-your-program) to measure
  only a subpart of your program.

  If you would like a dedicated integration for your language, let us know on
  [Discord](https://discord.gg/MxpaCfKSqF) or
  [open a GitHub issue](https://github.com/CodSpeedHQ/codspeed/issues/new).
</Note>

## Installing the CodSpeed CLI

Install the CodSpeed CLI using the installation script:

```sh theme={null}
curl -fsSL https://codspeed.io/install.sh | sh
```

After installation, authenticate with your CodSpeed account by running
`codspeed auth login`.

## Defining the benchmarks

Create a `codspeed.yml` file at the root of your repository. Each item in the
`benchmarks` list describes a command to benchmark:

```yaml codspeed.yml theme={null}
$schema: https://raw.githubusercontent.com/CodSpeedHQ/codspeed/refs/heads/main/schemas/codspeed.schema.json

benchmarks:
  - name: parse large file
    exec: ./my-tool parse fixtures/large.json

  - name: render template
    exec: ./my-tool render fixtures/template.html
    options:
      max-time: 2s
```

The `exec` field is the command to run. Build the executable beforehand with
your usual toolchain, CodSpeed measures the command as is.

See the [CLI configuration reference](/cli#configuration) for all available
fields and options.

## Running the benchmarks locally

Build your program, then run all the benchmarks defined in the configuration
file with the [walltime instrument](/instruments/walltime):

```shellsession title=terminal icon="square-terminal" theme={null}
$ codspeed run -m walltime

►►► Running the benchmarks

Executing: parse large file
Completed 13 warmup rounds
Warmup done, now performing 20 rounds

Executing: render template
Completed 23 warmup rounds
Warmup done, now performing 68 rounds

►►► Uploading results
Linked repository: [...]
Performance data uploaded

►►► Benchmark results

┌──────────────────┬─────────────┐
│ Benchmark        │ Measurement │
├──────────────────┼─────────────┤
│ parse large file │ 14.10 ms    │
├──────────────────┼─────────────┤
│ render template  │ 6.91 ms     │
└──────────────────┴─────────────┘

To see the full report, visit: https://app.codspeed.io/[...]
```

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

The walltime instrument runs on
[CodSpeed Macro Runners](/features/macro-runners), bare-metal machines that
deliver low-variance measurements:

<CIWorkflow
  mode="walltime"
  runsOn="codspeed-macro"
  buildSteps={[
"# ...",
"# Setup your environment here:",
"#  - Install your language toolchain",
"#  - Install your dependencies",
"#  - Build your executable",
"# ...",
]}
  benchmarkCommand={[]}
/>

<Info>
  Contrary to other CI usages, **the `run` input is intentionally omitted**
  here: this is what makes the action run the benchmarks defined in your
  configuration file.
</Info>

## Advanced

### Benchmarking a subpart of your program

Benchmarks defined with `exec` measure the whole command, including process
startup. To measure only a specific section of your program, e.g., its core
processing loop, you can build a custom harness with the
[`instrument-hooks`](https://github.com/CodSpeedHQ/instrument-hooks) library.

A custom harness instruments your program directly: it tells CodSpeed exactly
when the measured section starts and stops. The library is a single C file that
integrates with virtually any language through FFI. Follow the
[custom harness guide](https://github.com/CodSpeedHQ/instrument-hooks/blob/main/CUSTOM_HARNESS.md)
to build one.

Since your program then embeds its own harness, declare it with `entrypoint`
instead of `exec` in the configuration file:

```yaml codspeed.yml theme={null}
benchmarks:
  - name: parse large file
    exec: ./my-tool parse fixtures/large.json # [!code --]
    entrypoint: ./my-tool parse fixtures/large.json # [!code ++]
```

Similarly, to benchmark a harness-equipped command without a configuration file,
use `codspeed run` instead of `codspeed exec`:

```sh theme={null}
codspeed run -m walltime -- ./my-tool parse fixtures/large.json
```

## Next Steps

<CardGroup cols={2}>
  <Card title="CodSpeed CLI" icon="square-terminal" href="/cli">
    Explore all the commands, instruments, and configuration options of the
    CodSpeed CLI.
  </Card>

  <Card title="Walltime instrument" icon="stopwatch" href="/instruments/walltime">
    Learn more about the walltime instrument and how to use it.
  </Card>

  <Card title="Profiling" icon="bars-sort" href="/features/profiling">
    Learn how to use flamegraphs and profiling data to optimize your code.
  </Card>

  <Card title="Performance checks" icon="circle-check" href="/features/performance-checks">
    Catch regressions automatically on every pull request.
  </Card>
</CardGroup>
