Node.js Micro-benchmarking Suite
Bench-node is a powerful Node.js module specifically designed for micro-benchmarking JavaScript code blocks, accurately measuring operations per second (ops/sec). Its current stable version is 0.14.0, and it maintains an active development pace with frequent minor releases every few weeks to months, indicating robust maintenance. A primary differentiator of `bench-node` is its explicit use of V8 deoptimization (via `%NeverOptimizeFunction`) to ensure that benchmarked code is not aggressively optimized away by the V8 engine. While this approach yields highly stable and reproducible results for micro-benchmarks, users should be aware that these "accurate" measurements might not perfectly reflect "realistic" performance in a fully optimized production environment. To further assist developers, the library includes an opt-in Dead Code Elimination (DCE) detection plugin that warns if benchmarked code is being optimized out, although enabling it disables V8 deoptimization. Additionally, `bench-node` offers statistical significance testing (Welch's t-test) to evaluate if observed performance differences are statistically meaningful, a crucial feature for benchmarking in high-variance environments. It provides a rich set of built-in reporters (text, chart, HTML, JSON, CSV, pretty) for result visualization and ships with TypeScript types for enhanced developer experience. Other features include support for setup/teardown routines, execution in worker threads for isolation, and both operations and time-based benchmarking modes.
Common errors
-
Dead code elimination detected. Result of benchmark might be inaccurate.
cause The V8 engine has optimized away or partially eliminated the code being benchmarked because its return value or side effects are not used.fixEnsure the benchmarked function's result is used or has observable side effects. For example, assign the result to a variable that is then used in a conditional `if (result !== expected) throw new Error('Unexpected');` or return the result from the benchmark function. Enable `detectDeadCodeElimination` in suite options to get these warnings. -
TypeError: (0, _bench_node.Suite) is not a constructor
cause This error typically indicates an incorrect import statement, often when attempting to use a CommonJS `require()` syntax with a module that is primarily designed for ES Modules (ESM) or when a named export is treated as a default export.fixIf using ES Modules, ensure you're using `import { Suite } from 'bench-node';`. If in a CommonJS environment, use `const { Suite } = require('bench-node');` to correctly destructure the named export. Ensure your build configuration (e.g., TypeScript, Babel) handles module interop correctly. -
Benchmark results show extremely high ops/sec (e.g., millions or billions) for trivial operations, seeming unrealistic.
cause The V8 JIT compiler has likely optimized away the benchmarked code entirely or partially because it detects no observable side effects or the return value isn't used, leading to misleadingly fast results.fixModify your benchmark code to ensure that the operations performed have a visible effect or that their results are consumed. For instance, return the result, store it in an external variable (if not part of the timed operation), or perform a simple check like `if (result === undefined) throw new Error();`. Using the `detectDeadCodeElimination` option can help identify such cases.
Warnings
- gotcha Bench-node's primary mechanism uses V8 deoptimization to make microbenchmarks more stable and reproducible. However, this means the code under test is explicitly prevented from undergoing V8's real-world optimizations, leading to 'accurate but not realistic' performance numbers.
- gotcha Dead Code Elimination (DCE) can significantly skew benchmark results if the V8 engine optimizes away portions of your benchmarked code because its output or side effects are not utilized. While `bench-node` provides an opt-in DCE detection plugin, enabling it disables the `V8NeverOptimizePlugin`.
- breaking In `v0.13.0`, reporter modules were refactored to separate formatting logic from stdout printing. Direct use of reporters that previously printed to stdout might now require calling explicit `to<Format>()` methods (e.g., `toText()`, `toJson()`) for programmatic consumption.
- gotcha When using the `timer` argument for manual timing within a benchmark function, the setup code executed before `timer.start()` can also be deoptimized by V8, potentially impacting the accuracy of the setup phase's performance.
- gotcha Statistical significance testing (t-test mode) was introduced in v0.14.0. While useful for comparing benchmarks, it automatically sets `repeatSuite=30`, potentially increasing benchmark execution time significantly.
Install
-
npm install bench-node -
yarn add bench-node -
pnpm add bench-node
Imports
- Suite
const Suite = require('bench-node').Suite;import { Suite } from 'bench-node'; - textReport
const textReport = require('bench-node').textReport;import { textReport } from 'bench-node'; - chartReport
const chartReport = require('bench-node').chartReport;import { chartReport } from 'bench-node';
Quickstart
import { Suite, chartReport } from 'bench-node';
const suite = new Suite({
// Optionally configure the reporter
reporter: chartReport,
reporterOptions: {
printHeader: true // Controls whether system info header is printed
}
});
suite.add('Using Array.push', () => {
const arr = [];
for (let i = 0; i < 1000; i++) {
arr.push(i);
}
return arr.length; // Ensure result is used to prevent DCE
});
suite.add('Using Array literal concatenation', () => {
let arr = [];
for (let i = 0; i < 1000; i++) {
arr = [...arr, i];
}
return arr.length; // Ensure result is used to prevent DCE
});
suite.add('Using direct index assignment', () => {
const arr = [];
for (let i = 0; i < 1000; i++) {
arr[i] = i;
}
return arr.length; // Ensure result is used to prevent DCE
});
suite.run();