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

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/benchmark.js-plugin`](https://www.npmjs.com/package/@codspeed/benchmark.js-plugin)
and `benchmark` (if not already installed):

<NpmInstall packages="benchmark @codspeed/benchmark.js-plugin" />

## Usage

### Creating benchmarks

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

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

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

    const suite = withCodSpeed(new Benchmark.Suite());

    suite
      .add("fibonacci10", () => {
        fibonacci(10);
      })
      .add("fibonacci15", () => {
        fibonacci(15);
      })
      .on("cycle", function (event: Benchmark.Event) {
        console.log(String(event.target));
      })
      .run();
    ```

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

      If you're working with CommonJS modules, you can totally use the `require`
      syntax for importing the libraries.
    </Tip>
  </Tab>

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

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

    const suite = withCodSpeed(new Benchmark.Suite());

    suite
      .add("fibonacci10", () => {
        fibonacci(10);
      })
      .add("fibonacci15", () => {
        fibonacci(15);
      })
      .on("cycle", function (event: Benchmark.Event) {
        console.log(String(event.target));
      })
      .run();
    ```
  </Tab>
</Tabs>

Here, a few things are happening:

* We create a simple recursive fibonacci function.
* We create a new `Benchmark.Suite` 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 with 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
    [CodSpeed] 2 benches detected but no instrumentation found
    [CodSpeed] falling back to benchmark.js
    fibonacci10 x 2,155,187 ops/sec ±0.50% (96 runs sampled)
    fibonacci15 x 194,742 ops/sec ±0.48% (95 runs sampled)
    ```
  </Tab>

  <Tab title="JavaScript">
    ```shellsession title=terminal icon="square-terminal" theme={null}
    $ node benches/bench.mjs
    [CodSpeed] 2 benches detected but no instrumentation found
    [CodSpeed] falling back to benchmark.js
    fibonacci10 x 2,155,187 ops/sec ±0.50% (96 runs sampled)
    fibonacci15 x 194,742 ops/sec ±0.48% (95 runs sampled)
    ```
  </Tab>
</Tabs>

And... Congrats🎉, CodSpeed is installed in your benchmarking suite! Locally,
CodSpeed will fall back to benchmark.js since the CPU simulation is only
available in the CI environment for now.

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 type { WithCodSpeedSuite } from "@codspeed/benchmark.js-plugin";
import { iterativeFibonacci } from "../../src/fibonacci";

export function registerFiboBenchmarks(suite: WithCodSpeedSuite) {
  suite
    .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/benchmark.js-plugin";
import Benchmark from "benchmark";
import { registerFiboBenchmarks } from "./fibo.bench";
import { registerFoobarbazBenchmarks } from "./foobarbaz.bench";

export const suite = withCodSpeed(new Benchmark.Suite());

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

  suite.on("cycle", function (event: Benchmark.Event) {
    console.log(String(event.target));
  });

  await suite.run({ async: true });
})();
```

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>
