---
title: "Performance Benchmarking"
description: "Performance benchmarking provides rigorous automated tooling to measure, track, and compare the developer workflow speed and bundler efficiency of Next.js and Turbopack. It solves the core problem ..."
last_updated: "2026-09-23T10:52:03.135025+00:00"
canonical_url: "https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/ecosystem-packages/performance-benchmarking"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [turbopack/packages/webpack-nmt/src/index.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts)
- [turbopack/packages/devlow-bench/src/cli.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts)
- [packages/next/src/server/dev/hot-reloader-turbopack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-turbopack.ts)
- [packages/next/src/server/dev/hot-reloader-webpack.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/hot-reloader-webpack.ts)
- [turbopack/packages/devlow-bench/src/index.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/index.ts)
- [turbopack/packages/devlow-bench/src/describe.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/describe.ts)
- [turbopack/packages/devlow-bench/src/types.d.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/types.d.ts)
- [packages/next/src/server/dev/on-demand-entry-handler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/on-demand-entry-handler.ts)
- [turbopack/packages/turbo-tracing-next-plugin/src/index.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/turbo-tracing-next-plugin/src/index.ts)
- [turbopack/packages/devlow-bench/src/runner.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts)
- [turbopack/packages/devlow-bench/src/interfaces/compare.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/compare.ts)
- [packages/next/src/server/dev/next-dev-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/next-dev-server.ts)
- [turbopack/packages/devlow-bench/package.json](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/package.json)
- [turbopack/packages/devlow-bench/src/browser.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts)
- [packages/next/src/server/client-component-renderer-logger.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/client-component-renderer-logger.ts)
- [turbopack/packages/devlow-bench/src/interfaces/datadog.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts)
- [turbopack/packages/devlow-bench/src/interfaces/console.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/console.ts)
- [packages/next/src/server/lib/router-utils/setup-dev-bundler.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/lib/router-utils/setup-dev-bundler.ts)
- [packages/next/src/cli/internal/turbo-trace-server.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts)
- [run-evals.js](https://github.com/blade47/next.js/blob/main/run-evals.js)
- [turbopack/packages/devlow-bench/src/interfaces/constants.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/constants.ts)
- [packages/next/src/cli/internal/upload-trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts)
- [packages/next/src/bundles/webpack/packages/FetchCompileWasmTemplatePlugin.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/FetchCompileWasmTemplatePlugin.js)
- [packages/next/src/trace/trace.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace.ts)
- [turbopack/packages/devlow-bench/src/interfaces/json.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/json.ts)
- [turbopack/packages/devlow-bench/src/interfaces/snapshot.ts](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/snapshot.ts)
- [packages/next/taskfile.js](https://github.com/blade47/next.js/blob/main/packages/next/taskfile.js)
- [packages/next/src/bundles/webpack/packages/FetchCompileWasmPlugin.js](https://github.com/blade47/next.js/blob/main/packages/next/src/bundles/webpack/packages/FetchCompileWasmPlugin.js)
- [turbopack/packages/devlow-bench/tsconfig.json](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/tsconfig.json)
- [packages/next/src/server/dev/use-cache-probe-pool.ts](https://github.com/blade47/next.js/blob/main/packages/next/src/server/dev/use-cache-probe-pool.ts)
</details>

## Overview

Performance benchmarking provides rigorous automated tooling to measure, track, and compare the developer workflow speed and bundler efficiency of Next.js and Turbopack. It solves the core problem of performance regression by combining repeatable CLI-driven scenario execution with headless browser automation, statistical timing aggregation, and multi-target metric exportation. Key design decisions include robust retry handling for flaky runs, modular interface architecture for reporting results to consoles, JSON outputs, Git snapshots, or Datadog, and integrated tracing plugins for both Webpack and Turbopack. It interacts closely with compiler instrumentation and diagnostic trace servers to provide deep insight into compilation spans, module resolution, and memory usage.

Sources: [turbopack/packages/devlow-bench/src/cli.ts:31-57](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts#L31-L57), [turbopack/packages/devlow-bench/src/index.ts:47-84](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/index.ts#L47-L84), [turbopack/packages/devlow-bench/src/runner.ts:29-222](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L29-L222), [turbopack/packages/devlow-bench/src/browser.ts:21-144](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L21-L144), [turbopack/packages/devlow-bench/src/interfaces/datadog.ts:31-103](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts#L31-L103), [turbopack/packages/webpack-nmt/src/index.ts:32-57](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L32-L57), [packages/next/src/cli/internal/turbo-trace-server.ts:114-130](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L114-L130)

## DevLow Architecture and CLI Interface

### DevLow Architecture and CLI Interface

### Overview

The `devlow-bench` package orchestrates developer workflow benchmarks via a CLI entry point that dispatches execution between `run` and `compare` subcommands. It configures scenario variations, validates parameters, and manages the execution runner lifecycle.

Sources: [turbopack/packages/devlow-bench/src/cli.ts:1-29](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts#L1-L29)

### CLI Command Orchestration

The CLI entry point parses `process.argv` using `minimist` and detects subcommands from the `SUBCOMMANDS` set, falling back to legacy run mode for back-compatibility when a bare script path is passed.

```
process.argv slicing → SUBCOMMANDS check ('run' | 'compare') → runCompareSubcommand / runRunSubcommand → minimist flag parsing → subcommand execution
```

Sources: [turbopack/packages/devlow-bench/src/cli.ts:10-57](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts#L10-L57)

### Benchmark Scenario Configuration and Runner Execution

The `runScenarios` execution engine processes scenario configurations by expanding Cartesian product permutations of variant properties. It enforces sample counts and warmup runs while handling retries up to `MAX_ATTEMPT_MULTIPLIER`.

```typescript
export async function runScenarios(
  scenarios: Scenario[],
  iface: Interface,
  options: { n?: number; warmup?: number } = {}
): Promise<void> {
  const n = Math.max(1, Math.floor(options.n ?? 1))
  const warmup = Math.max(0, Math.floor(options.warmup ?? 0))
  const fullIface = intoFullInterface(iface)
  // ... expands variants and iterates through warmup and sample runs ...
}
```

Sources: [turbopack/packages/devlow-bench/src/runner.ts:29-70](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L29-L70)

> [!WARNING]
> Do not enable `--warmup` when measuring cold-start metrics, as discarding the first N runs will invalidate cold-startup timing observations.

Sources: [turbopack/packages/devlow-bench/src/cli.ts:104-105](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts#L104-L105)

### CLI Options Reference

| Flag / Option | Alias | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| `--scenario` | `-s` | none | Only run scenarios matching the given name filter |
| `--interactive` | `-i` | `false` | Select scenarios and variants interactively |
| `--n` | none | `1` | Run each variant N times and report mean/p50/p90 |
| `--warmup` | none | `0` | Discard the first N runs of each variant before sampling |
| `--snapshot` | none | `./.devlow-bench/snapshots/<ts>.csv` | Override the output snapshot CSV path |
| `--compare` | none | `false` | Print a comparison table at end of run against latest snapshot |
| `--baseline` | none | none | Explicit baseline file or directory path (implies `--compare`) |
| `--json` | `-j` | none | Write benchmark results to the given JSON path |
| `--console` | none | `false` | Print execution results directly to the console |
| `--datadog` | none | none | Upload results to Datadog (requires `DATADOG_API_KEY`) |
| `--snowflake` | none | none | Upload results to Snowflake (requires topic and schema IDs) |
| `--help` | `-h`, `-?` | `false` | Show CLI usage instructions and option summaries |

Sources: [turbopack/packages/devlow-bench/src/cli.ts:80-120](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/cli.ts#L80-L120)

## Scenario Definition and Execution Engine

### Scenario Definition and Execution Engine

### Overview

The scenario authoring and execution engine manages benchmark definitions, lifecycle event hooks, and statistical timing aggregation. Scenarios are defined via the `describe` function, which registers configuration matrices and asynchronous runner functions. The execution loop iterates through variant configurations, handles execution attempts with retry boundaries, and computes statistical summaries.

Sources: [turbopack/packages/devlow-bench/src/describe.ts:16-63](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/describe.ts#L16-L63), [turbopack/packages/devlow-bench/src/runner.ts:70-201](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L70-L201)

### Execution Lifecycle and Measurement Call Chain

During a benchmark run, each scenario variant goes through an explicit sequence of interface lifecycle hooks and measurement passes. 

```
runScenarios() → variant iteration → withCurrent() context setup → wrappedIface.start() → 'start' timestamp measurement → variant.scenario.fn() → wrappedIface.end() → summary() statistics aggregation → fullIface.finish()
```

Sources: [turbopack/packages/devlow-bench/src/describe.ts:76-182](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/describe.ts#L76-L182), [turbopack/packages/devlow-bench/src/runner.ts:29-222](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L29-L222)

> [!NOTE]
> During warmup iterations, `collecting` is set to `false`, causing all measurements emitted via `reportMeasurement` or `measureTime` to be buffered per-run but discarded from final sample aggregation until `warmup` runs are exhausted.

Sources: [turbopack/packages/devlow-bench/src/runner.ts:78-163](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L78-L163)

### Interface Lifecycle Hooks and Full Interface Conversion

The `FullInterface` type defines the complete contract for reporting progress, start and end events, measurements, errors, and variant statistics. The `intoFullInterface` helper ensures missing optional hooks default to safe asynchronous no-ops.

| Interface Method | Parameters | Purpose |
| :--- | :--- | :--- |
| `filterScenarios` | `scenarios: Scenario[]` | Filters or reorders the registered scenario list |
| `filterScenarioVariants` | `scenarioVariants: ScenarioVariant[]` | Filters or reorders the expanded scenario variants |
| `start` | `scenario, props, runInfo` | Invoked when an individual scenario run begins |
| `measurement` | `scenario, props, name, value, unit, relativeTo` | Invoked when a metric measurement is reported |
| `end` | `scenario, props` | Invoked when a scenario run completes successfully |
| `error` | `scenario, props, error` | Invoked when a scenario run throws an exception |
| `variantStatistics` | `scenario, props, stats` | Invoked once per variant with per-metric stats (`samples`, `mean`, `p50`, `p90`) |
| `finish` | none | Invoked when all scenarios and variants have finished executing |

Sources: [turbopack/packages/devlow-bench/src/index.ts:47-99](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/index.ts#L47-L99)

> [!WARNING]
> Attempt failures inside `scenario.fn` trigger `wrappedIface.error` and discard the current attempt's measurements via `perRun` replacement without interrupting the wider variant sample collection loop, up to `MAX_ATTEMPT_MULTIPLIER`.

Sources: [turbopack/packages/devlow-bench/src/runner.ts:102-168](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/runner.ts#L102-L168)

### Full Worked Example: Scenario Definition and Measurement

The following example demonstrates how to author a benchmark scenario using `describe`, capture timing data with `measureTime`, and report custom metrics via `reportMeasurement`.

```typescript
import { describe, measureTime, reportMeasurement, PREVIOUS } from 'devlow-bench'

describe('compiler build pipeline', { mode: ['development', 'production'], incremental: [true, false] }, async (props) => {
  // Start an initial timing span
  const buildStart = Date.now()
  
  // Simulate compiler compilation task
  await Bun.sleep(props.mode === 'development' ? 50 : 200)
  
  // Measure elapsed time relative to the previous timing point or start
  await measureTime('build_duration', {
    relativeTo: PREVIOUS,
  })

  // Report a custom numerical memory measurement
  await reportMeasurement('peak_memory', 45.2, 'MB', {
    props: { incremental: props.incremental },
  })
})
```

Sources: [turbopack/packages/devlow-bench/src/describe.ts:16-182](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/describe.ts#L16-L182)

> [!IMPORTANT]
> When `relativeTo: PREVIOUS` is passed to `measureTime` or `reportMeasurement`, the execution engine automatically resolves `PREVIOUS` to the name of the most recent measurement sharing the same unit string within the current scenario context.

Sources: [turbopack/packages/devlow-bench/src/describe.ts:87-151](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/describe.ts#L87-L151)

## Browser Automation and Navigation Metrics

### Overview

Browser automation and navigation metric capture in `devlow-bench` is driven by Playwright Chromium sessions managed via the `BrowserSession` interface and its implementation `BrowserSessionImpl`. Sessions handle hard navigations, page reloads, and soft interactions like clicks, wrapping executions with automatic resource and performance monitoring.

Sources: [turbopack/packages/devlow-bench/src/browser.ts:12-17](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L12-L17), [turbopack/packages/devlow-bench/src/browser.ts:237-245](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L237-L245)

### Session Management and Navigation Timing

The `BrowserSessionImpl` class coordinates browser instances, contexts, and pages. Hard navigation flows through `hardNavigation(metricName, url)`, which initializes a page if absent, hooks request and console metrics via `withRequestMetrics`, and records high-resolution timing checkpoints against the `/start` anchor.

| Navigation Method | Parameters | Recorded Checkpoints & Lifecycle |
| :--- | :--- | :--- |
| `hardNavigation` | `metricName: string, url: string` | Records `/start`, `/html` (on commit), `/dom` (`domcontentloaded`), `/load` (`load`), and final idle duration. |
| `reload` | `metricName: string` | Reloads the current page context while preserving metrics tracking. |
| `softNavigationByClick` | `metricName: string, selector: string` | Triggers navigation via a DOM selector interaction. |
| `close` | none | Closes active pages, browser contexts, and the underlying browser instance. |

Sources: [turbopack/packages/devlow-bench/src/browser.ts:12-17](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L12-L17), [turbopack/packages/devlow-bench/src/browser.ts:237-285](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L237-L285)

> [!WARNING]
> Hard navigation throws an error immediately if `page.goto` produces no response or returns an HTTP response code outside the successful `200..299` range.

Sources: [turbopack/packages/devlow-bench/src/browser.ts:265-272](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L265-L272)

### Network Idle Tracking and Metric Capture

The internal `networkIdle` helper monitors pending network requests by listening to `request`, `requestfailed`, and `requestfinished` events on the Playwright page. It filters out server-sent events (`text/event-stream`), manages request reference counts, and enforces a grace delay (`delayMs = 300`) after all requests settle before resolving.

Concurrently, `withRequestMetrics` intercepts response bodies, categorizes transferred assets by file extension extracted via regular expressions, aggregates response sizes in bytes, tallies request counts, and tracks console message severities (`error`, `warning`, `log`, and `uncaught`).

```typescript
const browserSession: BrowserSession = new BrowserSessionImpl(browser, context)
const page = await browserSession.hardNavigation('app_load', 'http://localhost:3000')
await browserSession.softNavigationByClick('nav_click', 'button#load-more')
await browserSession.close()
```

Sources: [turbopack/packages/devlow-bench/src/browser.ts:21-144](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L21-L144), [turbopack/packages/devlow-bench/src/browser.ts:153-235](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/browser.ts#L153-L235)

## Benchmark Result Exporters and Comparison

### Overview

The `devlow-bench` reporting subsystem uses modular `Interface` plugins to export benchmark telemetry across multiple targets. Supported exporter interfaces include console reporting (`console`), JSON file output (`json`), Git-backed snapshot tracking (`snapshot`), historical baseline comparison (`compare`), and Datadog cloud distribution telemetry (`datadog`). Each interface implements a subset of lifecycle hooks—such as `start`, `measurement`, `variantStatistics`, `error`, `end`, and `finish`—allowing benchmarks to broadcast metrics to terminals, storage files, version control snapshots, and remote monitoring platforms concurrently.

Sources: [turbopack/packages/devlow-bench/src/interfaces/compare.ts:6-32](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/compare.ts#L6-L32), [turbopack/packages/devlow-bench/src/interfaces/datadog.ts:31-103](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts#L31-L103), [turbopack/packages/devlow-bench/src/interfaces/console.ts:8-66](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/console.ts#L8-L66), [turbopack/packages/devlow-bench/src/interfaces/json.ts:18-94](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/json.ts#L18-L94), [turbopack/packages/devlow-bench/src/interfaces/snapshot.ts:27-68](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/snapshot.ts#L27-L68)

### Exporter Interfaces and Configuration Options

Each exporter interface is initialized via a factory function that accepts configuration options, environment variables, or file paths.

| Exporter Module | Primary Constructor / Factory | Environment Variables / Options | Output Target & Behavior |
| :--- | :--- | :--- | :--- |
| `console` | `createInterface(options?: { n?: number })` | `options.n` (default `1`) | Prints formatted variant headers, live run progress, and statistical summaries (`mean`, `p50`, `p90`) to stdout using `picocolors`. |
| `json` | `createInterface(file?: string, options?: { n?: number })` | `JSON_OUTPUT_FILE` (required if `file` omitted), `options.n` | Aggregates samples and writes JSON results or multi-run summary objects via `fs/promises` `writeFile`. |
| `snapshot` | `createInterface(options?: { path?: string })` | `GITHUB_SHA`, `GITHUB_REF_NAME` | Resolves git metadata (`sha`, `branch`) and appends rows to a persistent snapshot file via `writeSnapshot`. |
| `compare` | `createInterface(options: { baselinePath: string })` | `options.baselinePath` (required) | Reads a baseline snapshot, groups rows, and prints comparative performance analysis against current variant samples. |
| `datadog` | `createInterface(options?: { apiKey?, appKey?, host? })` | `DATADOG_API_KEY`, `DATADOG_APP_KEY`, `DATADOG_HOST` | Submits distribution points series to Datadog V1 Metrics API with system, hardware, and git tags. |

Sources: [turbopack/packages/devlow-bench/src/interfaces/compare.ts:6-32](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/compare.ts#L6-L32), [turbopack/packages/devlow-bench/src/interfaces/datadog.ts:31-103](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts#L31-L103), [turbopack/packages/devlow-bench/src/interfaces/console.ts:8-66](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/console.ts#L8-L66), [turbopack/packages/devlow-bench/src/interfaces/constants.ts:10-24](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/constants.ts#L10-L24), [turbopack/packages/devlow-bench/src/interfaces/json.ts:18-38](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/json.ts#L18-L38), [turbopack/packages/devlow-bench/src/interfaces/snapshot.ts:10-30](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/snapshot.ts#L10-L30)

### Datadog Telemetry and Common System Tags

The Datadog exporter constructs a shared set of environment tags during initialization by querying system metadata constants and Git state. The generated tag set includes CI execution status (`ci`), operating system platform (`os`) and release (`os_release`), CPU count (`cpus`), CPU model (`cpu_model`), current username (`user`), CPU architecture (`arch`), total system memory rounded to gigabytes (`total_memory`), Node.js version (`node_version`), and Git commit SHA (`git_sha`) and branch (`git_branch`). 

Incoming metric names are normalized via `toIdentifier`, replacing slashes with dots and spaces with underscores. Unit strings such as `ms`, `requests`, and `bytes` are mapped to Datadog metadata unit types (`millisecond`, `request`, and `byte`). During measurement reporting, distribution points are accumulated in memory and submitted in bulk when `end` is invoked.

```typescript
const datadogIface = datadogInterface({
  apiKey: process.env.DATADOG_API_KEY,
  appKey: process.env.DATADOG_APP_KEY,
  host: 'ci-runner-01',
})
```

Sources: [turbopack/packages/devlow-bench/src/interfaces/datadog.ts:21-103](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts#L21-L103), [turbopack/packages/devlow-bench/src/interfaces/constants.ts:1-34](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/constants.ts#L1-L34)

> [!WARNING]
> The Datadog interface throws an immediate runtime error during initialization if `DATADOG_API_KEY` is missing from the environment or options.

Sources: [turbopack/packages/devlow-bench/src/interfaces/datadog.ts:36-38](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/datadog.ts#L36-L38)

### Snapshot Export and Baseline Comparison

The `snapshot` interface collects sample rows during `variantStatistics` execution, tagging each individual sample with its index, value, unit, and relative baseline reference. When `finish` runs, it calls `readGitInfo` to resolve the current Git SHA and branch (falling back to `GITHUB_SHA` and `GITHUB_REF_NAME` environment variables or executing `git rev-parse` via child processes), updates all accumulated rows with these Git identifiers, and writes them out using `writeSnapshot`.

Conversely, the `compare` interface reads an existing baseline snapshot path via `readSnapshot`, groups rows using `groupRows`, and registers variant statistics into a `current` map. When the benchmark finishes, `printComparison` evaluates differences between the baseline and current metrics.

Sources: [turbopack/packages/devlow-bench/src/interfaces/compare.ts:6-32](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/compare.ts#L6-L32), [turbopack/packages/devlow-bench/src/interfaces/snapshot.ts:10-68](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/snapshot.ts#L10-L68)

> [!NOTE]
> When `n === 1`, the JSON exporter outputs individual measurements with a single-run schema containing `key`, `value`, `unit`, `text`, `datapoints: 1`, and `relativeTo`. When `n > 1`, it wraps an array of computed statistical aggregates including `mean`, `p50`, `p90`, and the raw `samples` array inside a `{ results }` payload object.

Sources: [turbopack/packages/devlow-bench/src/interfaces/json.ts:28-90](https://github.com/blade47/next.js/blob/main/turbopack/packages/devlow-bench/src/interfaces/json.ts#L28-L90)

## Webpack and Turbopack Tracing Plugins

### Overview

Compiler instrumentation plugins track node module resolution and execution performance during builds. The `NodeModuleTracePlugin` and `createNodeFileTrace` integrate with Webpack and Next.js compilation pipelines to execute `@vercel/experimental-nft` file tracing on output chunks. Additionally, trace span utilities provide granular timing measurement and hierarchical telemetry for asynchronous operations.

Sources: [turbopack/packages/webpack-nmt/src/index.ts:6-140](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L6-L140), [turbopack/packages/turbo-tracing-next-plugin/src/index.ts:1-27](https://github.com/blade47/next.js/blob/main/turbopack/packages/turbo-tracing-next-plugin/src/index.ts#L1-L27), [packages/next/src/trace/trace.ts:30-154](https://github.com/blade47/next.js/blob/main/packages/next/src/trace/trace.ts#L30-L154)

### Trace Configuration and Chunk Resolution

The `NodeModuleTracePlugin` accepts configuration options through `NodeModuleTracePluginOptions`. During the compilation lifecycle, `apply()` hooks into `compiler.hooks.compilation` to tap into `compilation.hooks.processAssets` at stage `Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE`, triggering `createTraceAssets()` which inspects entrypoints and populates `chunksToTrace`.

Sources: [turbopack/packages/webpack-nmt/src/index.ts:6-57](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L6-L57)

| Option Property | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `cwd` | `string` | `process.cwd()` | Working directory for the tracing process |
| `contextDirectory` | `string` | `npm_config_local_prefix` / `PROJECT_CWD` / `cwd` | Context directory passed to `node-file-trace` |
| `path` | `string` | `undefined` | Additional PATH entries for binary resolution |
| `maxFiles` | `number` | `128` | Maximum number of files batched per trace invocation |
| `log.all` | `boolean` | `undefined` | Passes `--show-all` to the trace runner |
| `log.detail` | `boolean` | `undefined` | Passes `--log-detail` to the trace runner |
| `log.level` | string enum | `undefined` | Sets log level (`bug`, `fatal`, `error`, `warning`, `hint`, `note`, `suggestions`, `info`) |

Sources: [turbopack/packages/webpack-nmt/src/index.ts:6-30](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L6-L30)

> [!WARNING]
> Files ending in `.wasm` or `.map` are explicitly filtered out by `isTraceable` and will never be added to `chunksToTrace`.

Sources: [turbopack/packages/webpack-nmt/src/index.ts:62-70](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L62-L70)

### Tracing Execution Pipeline

Once compilation emits files, `compiler.hooks.afterEmit` invokes `runTrace()`, which executes the `node-file-trace` binary using chunk batching.

The tracing invocation lifecycle proceeds as follows: `runTrace()` resolves binary paths via `require.resolve()` for `@vercel/experimental-nft/package.json` and its platform-specific binary package `@vercel/experimental-nft-${process.platform}-${process.arch}/package.json` → `traceChunks()` spawns the `node-file-trace` child process with `spawn('node-file-trace', [...args, ...chunks])` → event listeners pipe `stdout` and `stderr` to process streams → `exit` event resolves or rejects the execution promise.

Sources: [turbopack/packages/webpack-nmt/src/index.ts:54-174](https://github.com/blade47/next.js/blob/main/turbopack/packages/webpack-nmt/src/index.ts#L54-L174)

```typescript
import { createNodeFileTrace } from '@vercel/turbo-tracing-next-plugin'

const withTrace = createNodeFileTrace({
  maxFiles: 64,
  log: { level: 'info', detail: true }
})
```

Sources: [turbopack/packages/turbo-tracing-next-plugin/src/index.ts:7-27](https://github.com/blade47/next.js/blob/main/turbopack/packages/turbo-tracing-next-plugin/src/index.ts#L7-L27)

## Trace Server and Diagnostics Management

### Overview

Internal diagnostic management provides HTTP trace server visualization, memory profiling summaries, and blob storage upload tooling to diagnose performance bottlenecks and runtime memory usage. The trace server tooling interacts with native SWC bindings and telemetry spans to inspect trace files, format execution durations, summarize TurboMalloc live bytes, and upload performance profiles securely.

Sources: [packages/next/src/cli/internal/turbo-trace-server.ts:1-122](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L1-L122), [packages/next/src/cli/internal/upload-trace.ts:1-116](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts#L1-L116)

### Trace Server Utilities and Memory Profiling

The trace server maps internal ticks to human-readable units where 100 internal ticks equal 1 microsecond. Durations are formatted via `formatDuration()` into microseconds (`µs`), milliseconds (`ms`), or seconds (`s`), while relative timings handle negative offsets when child spans precede their parent reference point. Memory consumption is tracked via `summarizeMemorySamples()` using TurboMalloc live sample arrays.

Sources: [packages/next/src/cli/internal/turbo-trace-server.ts:13-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L13-L63)

| Helper Function | Input Parameter Type | Return Type | Purpose |
| :--- | :--- | :--- | :--- |
| `formatDuration` | `number` (ticks) | `string` | Formats duration ticks into `µs`, `ms`, or `s` |
| `formatRelative` | `number` (ticks) | `string` | Formats relative start/end offsets supporting negative values |
| `formatBytes` | `number` (bytes) | `string` | Converts raw byte counts into human-readable B, KB, MB, or GB |
| `summarizeMemorySamples` | `number[][]` | `string \| null` | Computes min, max, start, end, delta, and maxPressure from memory samples |

Sources: [packages/next/src/cli/internal/turbo-trace-server.ts:18-63](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L18-L63)

> [!NOTE]
> The trace server requires native non-WASM SWC bindings; attempting to start the server when native bindings fail to load writes an error message to `console.error` and terminates the process with exit code `1`.

Sources: [packages/next/src/cli/internal/turbo-trace-server.ts:114-130](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/turbo-trace-server.ts#L114-L130)

### Blob Storage Upload Tooling

Diagnostic profiles and trace files generated during builds can be uploaded to remote storage via `uploadTraceToBlob()`. The tool scans the `.next-profiles` directory for `.cpuprofile` and `trace-turbopack.bin` files, validates file headers, requests authorization tokens, and streams contents with progress feedback.

Sources: [packages/next/src/cli/internal/upload-trace.ts:1-141](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts#L1-L141)

The trace upload execution sequence proceeds as follows: `uploadTraceToBlob()` reads the `.next-profiles` directory via `fs.readdir()` → filters entries matching `.cpuprofile` or `trace-turbopack.bin` → reads file headers into a 16-byte buffer via `fs.open()` and `fd.read()` → validates V8 CPU profile headers (`{"nodes":`) or Turbopack trace headers (`TRACEv0`) via `validateCpuProfile()` or `validateTurbopackTrace()` → sends a `POST` request to `getUploadUrl()` to acquire an upload token → executes private bucket upload via `put()` with chunked progress streams or full buffers.

Sources: [packages/next/src/cli/internal/upload-trace.ts:56-196](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts#L56-L196)

```typescript
import { uploadTraceToBlob } from 'next/dist/cli/internal/upload-trace'

await uploadTraceToBlob({
  directory: process.cwd(),
})
```

Sources: [packages/next/src/cli/internal/upload-trace.ts:14-84](https://github.com/blade47/next.js/blob/main/packages/next/src/cli/internal/upload-trace.ts#L14-L84)

## Related

- [Bundler Integration](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/technical/development-and-diagnostics/bundler-integration)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/8f4009b0-65bd-4480-9b00-e201f0914bb3/llms.txt) for all pages in this wiki.
