LPM CLI

lpm bench

Run project benchmarks with Vitest or a package script, including selected workspace members.

Use lpm bench to measure code performance with the benchmark runner declared by your project.

lpm bench [OPTIONS] [ARGS]...

Quickstart

lpm install -D vitest
lpm bench
lpm bench src/parser.bench.ts
ModeCommand
Run the current package oncelpm bench
Run every workspace memberlpm bench --all
Run selected memberslpm bench --filter parser
Watch one memberlpm bench --filter parser -- --watch
Get a JSON resultlpm --json bench

Runner selection

If vitest appears in dependencies or devDependencies, LPM CLI runs vitest bench --run. Otherwise, it runs the bench script from package.json. A declared Vitest dependency takes priority over the script.

Detection does not install a runner. If the selected executable is missing, run lpm install. For explicit package-script execution, use lpm run bench.

Project configuration

lpm.json has no dedicated bench section. Project environment configuration and managed runtime pins still apply.

lpm.json
{
  "runtime": { "node": "22" },
  "env": { "bench": ".env.bench" }
}

For package-script fallback, tasks.bench.env takes priority over env.bench. The script uses prebench and postbench hooks and npm lifecycle variables. A failed phase stops later phases. Extra arguments apply to the main script.

Direct Vitest execution uses the default project environment. Both paths apply environment schemas and managed runtime selection. Workspace members inherit root runtime pins and can override each runtime locally. A strict engines.node mismatch stops the run before the benchmark starts.

The dedicated command does not use task commands, task caching, or task dependencies. Use lpm run for those workflows. See managed runtimes for automatic downloads and fallback behavior.

Write a benchmark

src/parser.bench.ts
import { bench, describe } from 'vitest';

const input = '{"name":"example","count":100}';

describe('parse configuration', () => {
  bench('JSON.parse', () => {
    JSON.parse(input);
  });
});
lpm bench src/parser.bench.ts

For another framework, define a package script:

package.json
{
  "scripts": {
    "bench": "node benchmarks/parser.mjs"
  }
}
lpm run bench

If Vitest is absent from the manifest, lpm bench also uses this script. The script controls its own benchmark behavior and must exit for a finite CI run.

Watch and CI

Vitest benchmarks run once by default, including in an interactive terminal. An explicit watch request removes the default --run argument.

lpm bench -- --watch
lpm bench --filter parser -- --watch
lpm --json bench --all

A workspace watch request can select exactly one member. LPM CLI rejects requests that select multiple members. An explicit long watch flag with an empty selection reports that there is nothing to watch.

Vitest short-option bundles such as -uw also request watch mode. Tokens after a second -- belong to the runner and do not select watch mode.

Use --watch=false to disable Vitest watch mode. Vitest treats values such as --watch=off and --watch=0 as enabled.

Put benchmark options after --:

lpm bench -- --reporter=json --outputFile=bench-results.json

The runner writes its own report file. Global --json controls the LPM CLI result envelope.

Workspaces

lpm bench --all
lpm bench --filter '@scope/*'
lpm bench --filter parser --filter serializer
lpm bench --filter-prod ...shared
lpm bench --affected --base origin/main
lpm bench --filter parser --fail-if-no-match
lpm bench --all --workspace-concurrency 1

Detection runs separately for each selected member. Members run in dependency order. Independent members can run together. A failed member does not stop the remaining selected members.

--all cannot combine with filters or --affected. Filters and --affected combine as a union. --filter-prod excludes development edges from dependency closures.

Concurrency uses the CLI flag, then root lpm.toml [workspace].concurrency, then global workspace-concurrency, then available parallelism. This limit does not control the benchmark runner's own workers. For measurements that require an idle machine, run one member at a time. See workspace filters.

Output and recovery

Human mode streams benchmark output to the terminal. With --json, single-package and workspace runs emit one result after completion. Failed members include captured output or a launch error. Successful members omit captured output. Each captured stream has a 10 MiB limit, and JSON mode closes stdin.

A single-package run preserves the runner or hook exit code. If any selected member fails, a workspace run exits with status 1. Each failed member retains its own exit code. A launch or configuration failure has exit_code: null and an error message. Failures before member preparation use the global error object.

On Unix, stop signals trigger child cleanup before LPM CLI exits. SIGINT produces status 130, and SIGTERM produces 143. A daemon that detaches and outlives its parent manages its own shutdown.

If no runner exists, install Vitest or define a bench script. If a workspace filter matches no members, add --fail-if-no-match to catch the mismatch in CI. See test and bench runners for shared environment, runtime, and cleanup behavior.

Flags

FlagEffect
--allSelect every workspace member
--filter <FILTER>Select members with the workspace filter grammar. Repeat to combine selections
--filter-prod <FILTER_PROD>Select members with production dependency closures
--affectedSelect members affected by Git changes
--base <BASE>Set the Git base for --affected. Default: main
--changed-files-ignore-pattern <CHANGED_FILES_IGNORE_PATTERN>Ignore matching changed paths during Git-based selection
--test-pattern <TEST_PATTERN>Treat matching changed paths as test-only during affected selection
--fail-if-no-matchIf no workspace member matches, fail
--workspace-concurrency <N>Limit concurrent workspace members

Runner flags belong after --. See global flags.

See also