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

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 NpmInstall = ({packages}) => {
  return <CodeGroup>
      <code className="language-sh" filename="npm">
      npm install --save-dev {packages}
      </code>

      <code className="language-sh" filename="yarn">
      yarn add --dev {packages}
      </code>

      <code className="language-sh" filename="pnpm">
      pnpm add -D {packages}
      </code>
    </CodeGroup>;
};

## Installation

First install the plugin
[`@codspeed/tinybench-plugin`](https://www.npmjs.com/package/@codspeed/tinybench-plugin)
and `tinybench` (if not already installed):

<Info>
  The CodSpeed plugin now requires `tinybench` v4 and above. Note that tinybench
  v4 has dropped support for CommonJS modules, so ESM is now required.
</Info>

<NpmInstall packages="tinybench @codspeed/tinybench-plugin" />

## Usage

### Creating benchmarks

Let's create a fibonacci function and benchmark it with tinybench and the
CodSpeed plugin:

<Tabs>
  <Tab title="TypeScript">
    ```typescript benches/bench.ts {11} theme={null}
    import { withCodSpeed } from "@codspeed/tinybench-plugin";
    import { Bench } from "tinybench";

    function fibonacci(n: number): number {
      if (n < 2) {
        return n;
      }
      return fibonacci(n - 1) + fibonacci(n - 2);
    }

    const bench = withCodSpeed(new Bench());

    bench
      .add("fibonacci10", () => {
        fibonacci(10);
      })
      .add("fibonacci15", () => {
        fibonacci(15);
      });

    await bench.run();
    console.table(bench.table());
    ```
  </Tab>

  <Tab title="JavaScript">
    ```js benches/bench.mjs {11} theme={null}
    import { Bench } from "tinybench";
    import { withCodSpeed } from "@codspeed/tinybench-plugin";

    function fibonacci(n) {
      if (n < 2) {
        return n;
      }
      return fibonacci(n - 1) + fibonacci(n - 2);
    }

    const bench = withCodSpeed(new Bench());

    bench
      .add("fibonacci10", () => {
        fibonacci(10);
      })
      .add("fibonacci15", () => {
        fibonacci(15);
      });

    await bench.run();
    console.table(bench.table());
    ```

    <Tip>
      Noticed the `.mjs` extension? This is because we're using the ESM module format.
      Saving our file with the `.js` extension would have worked as well, but we would
      have needed to add `"type": "module"` to our `package.json` file to instruct
      Node.js to use the ESM module format.
    </Tip>
  </Tab>
</Tabs>

Here, a few things are happening:

* We create a simple recursive fibonacci function.

* We create a new `Bench` instance with CodSpeed support by using the
  **`withCodSpeed`** helper. This step is **critical** to enable CodSpeed on
  your benchmarks.

* We add two benchmarks to the suite and launch it, benching our `fibonacci`
  function for 10 and 15.

### Testing the benchmarks locally

Now, we can run our benchmarks locally to make sure everything is working as
expected:

<Tabs>
  <Tab title="TypeScript">
    <Tip>
      **TypeScript runner**

      To run the `.ts` file directly, we recommend using
      [`esbuild-register`](https://github.com/egoist/esbuild-register). It allows
      running TypeScript & ESM files directly with Node.js.

      <NpmInstall packages="esbuild-register" />
    </Tip>

    ```shellsession title=terminal icon="square-terminal" theme={null}
    $ node -r esbuild/register benches/bench.ts
    ┌─────────┬───────────────┬───────────────────┬──────────┐
    │ (index) │   Task Name   │ Average Time (ns) │  Margin  │
    ├─────────┼───────────────┼───────────────────┼──────────┤
    │    0    │ 'fibonacci10' │ 552.4139857896414 │ '±0.18%' │
    │    1    │ 'fibonacci15' │ 5633.276191749634 │ '±0.14%' │
    └─────────┴───────────────┴───────────────────┴──────────┘
    ```
  </Tab>

  <Tab title="JavaScript">
    ```shellsession title=terminal icon="square-terminal" theme={null}
    $ node benches/bench.mjs
    ┌─────────┬───────────────┬───────────────────┬──────────┐
    │ (index) │   Task Name   │ Average Time (ns) │  Margin  │
    ├─────────┼───────────────┼───────────────────┼──────────┤
    │    0    │ 'fibonacci10' │ 552.4139857896414 │ '±0.18%' │
    │    1    │ 'fibonacci15' │ 5633.276191749634 │ '±0.14%' │
    └─────────┴───────────────┴───────────────────┴──────────┘
    ```
  </Tab>
</Tabs>

And... Congrats🎉, CodSpeed is installed in your benchmarking suite! When not
used in the CI environment or with the
[CLI](https://github.com/CodSpeedHQ/codspeed), CodSpeed will fallback to using
the default `tinybench`.

You can now
[run those benchmarks in your CI](#running-the-benchmarks-in-your-ci) to get
consistent performance measurements.

### Integrating into a bigger project, multiple benchmark files

Often time you will not be writing your benchmarks in a single file. Indeed, it
can become quite difficult to maintain a single file with all your benchmarks as
your project grows.

<Note>
  You can find the source code for the following example in the
  [examples of the `codspeed-node` repository](https://github.com/CodSpeedHQ/codspeed-node/tree/main/examples).
  There are multiple examples available, for CJS, ESM, JavaScript, and TypeScript.
</Note>

For these kind of situations, we recommend the following approach. Let's say you
have a file structure like this, in a project with **TypeScript**:

```text file-structure theme={null}
.
├── bench
│   ├── fibo.bench.ts
│   ├── foobarbaz.bench.ts
│   └── index.bench.ts
├── package.json
├── src
│   ├── fibonacci.ts
│   └── foobarbaz.ts
└── tsconfig.json
```

* The `src` directory contains the source code of the project. Here we have two
  files, `fibonacci.ts` and `foobarbaz.ts`.
* The `bench` directory contains the benchmarks for the project. There is a file
  for each source file that defines benchmarks for it.
* The `bench/index.bench.ts` file is the entry point for the benchmarks. It
  imports all the other benchmark files and runs them.

```typescript bench/fibo.bench.ts theme={null}
import { Bench } from "tinybench";
import { iterativeFibonacci } from "../../src/fibonacci";

export function registerFiboBenchmarks(bench: Bench) {
  bench
    .add("test_iterative_fibo_10", () => {
      iterativeFibonacci(10);
    })
    .add("test_iterative_fibo_100", () => {
      iterativeFibonacci(100);
    });
}
```

Here we define a function that takes an instance of `Bench` as a parameter and
then adds some benchmarks to it. This will allow us to add benchmarks to the
same suite from multiple files.

```typescript bench/index.bench.ts theme={null}
import { withCodSpeed } from "@codspeed/tinybench-plugin";
import { Bench } from "tinybench";
import { registerFiboBenchmarks } from "./fibo.bench";
import { registerFoobarbazBenchmarks } from "./foobarbaz.bench";

export const bench = withCodSpeed(new Bench());

(async () => {
  registerFiboBenchmarks(bench);
  registerFoobarbazBenchmarks(bench);

  await bench.run();
  console.table(bench.table());
})();
```

Here all the functions registering benchmarks are executed to import all the
benchmarks from the different files.

To run the benchmarks, use the following command:

```sh theme={null}
node -r esbuild-register bench/index.bench.ts
```

<Note>
  Check out the full for this example:
  [`with-typescript-cjs` in the `codspeed-node` repository](https://github.com/CodSpeedHQ/codspeed-node/tree/main/examples/with-typescript-cjs).
</Note>

### 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
  buildSteps={[
"- uses: actions/setup-node@v6",
"- name: Install dependencies",
"  run: npm install",
]}
  mode="simulation"
  benchmarkCommand={["node benches/bench.mjs"]}
/>

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

## Limitations

### Async code execution profiling

[Execution profiles](/features/profiling) for async code can sometimes be
unreliable, as profiling tools may lose stack trace information due to the event
loop. If your code is fully sync, consider using tinybench's `runSync` as an
entrypoint to improve accuracy.
