Loading lesson path
The perf_hooks module provides a set of APIs for performance measurement based on the W3C Performance Timeline specification.
The module includes several useful features such as high-resolution timers, performance marks, measures, observers, and histograms.
To use the Performance Hooks module, you need to require it in your code:
// Import the entire module const { performance, PerformanceObserver } = require('perf_hooks');
// Or using destructuring for specific parts const { performance } = require('perf_hooks');The most basic use of the performance API is to measure elapsed time with high precision:
const { performance } = require('perf_hooks');
// Get the current high-resolution time const startTime = performance.now();
// Perform some operation let sum = 0;
for (let i = 0; i < 1000000; i++) {
sum += i;
}
// Get the end time const endTime = performance.now();
// Calculate and display the elapsed time in milliseconds console.log(`Operation took ${(endTime - startTime).toFixed(2)} milliseconds`);
The performance.now()Formula
method returns a high - resolution timestamp in milliseconds, measured from the time the current Node.js process started.Performance marks are specific points in time that you want to track:
const { performance } = require('perf_hooks');
// Create marks at specific points in your code performance.mark('startProcess');
// Simulate some work let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i);
}
// Create another mark performance.mark('endProcess');
// Get all the marks console.log(performance.getEntriesByType('mark'));Performance measures calculate the time duration between two marks:
const { performance } = require('perf_hooks');
// Create a start mark performance.mark('start');
// Simulate some work let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sqrt(i);
}
// Create an end mark performance.mark('end');
// Create a measure between the two marks performance.measure('processTime', 'start', 'end');
// Get the measure const measure = performance.getEntriesByName('processTime')[0];
console.log(`Process took ${measure.duration.toFixed(2)} milliseconds`);
// Clear marks and measures performance.clearMarks();
performance.clearMeasures();PerformanceObserver allows you to observe performance events asynchronously:
const { performance, PerformanceObserver } = require('perf_hooks');
// Create a performance observer const obs = new PerformanceObserver((items) => {
// Process all entries const entries = items.getEntries();
entries.forEach((entry) => {
console.log(`Name: ${entry.name}, Type: ${entry.entryType}, Duration: ${entry.duration.toFixed(2)}ms`);
});
});
// Subscribe to specific entry types obs.observe({ entryTypes: ['measure'] });
// First task performance.mark('task1Start');
// Simulate work setTimeout(() => {
performance.mark('task1End');
performance.measure('Task 1', 'task1Start', 'task1End');
// Second task performance.mark('task2Start');
setTimeout(() => {
performance.mark('task2End');
performance.measure('Task 2', 'task2Start', 'task2End');
// Clean up performance.clearMarks();
performance.clearMeasures();
obs.disconnect();
}, 1000);
}, 1000);The Performance Timeline API provides methods to retrieve performance entries:
const { performance } = require('perf_hooks');
// Create some performance entries performance.mark('mark1');
performance.mark('mark2');
let sum = 0;
for (let i = 0; i < 100000; i++) {
sum += i;
}
performance.mark('mark3');
performance.measure('measure1', 'mark1', 'mark2');
performance.measure('measure2', 'mark2', 'mark3');
// Get all performance entries console.log('All entries:');
console.log(performance.getEntries());
// Get entries by type console.log('\nMarks:');
console.log(performance.getEntriesByType('mark'));
// Get entries by name console.log('\nMeasure 1:');
console.log(performance.getEntriesByName('measure1'));Node.js provides different performance timing APIs with varying levels of precision:
const { performance, monitorEventLoopDelay } = require('perf_hooks');
// 1. Date.now() - millisecond precision const dateStart = Date.now();
const dateEnd = Date.now();
console.log(`Date.now() difference: ${dateEnd - dateStart}ms`);
// 2. process.hrtime() - nanosecond precision const hrStart = process.hrtime();
const hrEnd = process.hrtime(hrStart);
console.log(`process.hrtime() difference: ${hrEnd[0]}s ${hrEnd[1]}ns`);
// 3. performance.now() - microsecond precision const perfStart = performance.now();
const perfEnd = performance.now();
console.log(`performance.now() difference: ${(perfEnd - perfStart).toFixed(6)}ms`);
// 4. Event loop delay monitoring (available in Node.js 12.0.0+)
const histogram = monitorEventLoopDelay({ resolution: 20 });
histogram.enable();
setTimeout(() => {
histogram.disable();
console.log('Event loop delay metrics:');
console.log(` Min: ${histogram.min}ns`);
console.log(` Max: ${histogram.max}ns`);
console.log(` Mean: ${histogram.mean.toFixed(2)}ns`);
console.log(` Stddev: ${histogram.stddev.toFixed(2)}ns`);
console.log(` Percentiles: 50=${histogram.percentile(50).toFixed(2)}ns, 99=${histogram.percentile(99).toFixed(2)}ns`);
}, 1000);The monitorEventLoopDelay function provides a way to monitor the delay in the event loop:
const { monitorEventLoopDelay } = require('perf_hooks');Formula
// Create a histogram const histogram = monitorEventLoopDelay({ resolution: 10 });// Enable monitoring histogram.enable();
// Simulate load on the event loop const operations = [];
for (let i = 0; i < 10; i++) {
operations.push(new Promise((resolve) => {
setTimeout(() => {
// Simulate CPU-intensive work let sum = 0;
for (let j = 0; j < 10000000; j++) {
sum += j;
}
resolve(sum);
}, 100);
}));
}// After all operations complete
Promise.all(operations).then(() => {
// Disable monitoring histogram.disable();
// Print statistics console.log('Event Loop Delay Statistics:');
console.log(` Min: ${histogram.min}ns`);
console.log(` Max: ${histogram.max}ns`);
console.log(` Mean: ${histogram.mean.toFixed(2)}ns`);
console.log(` Stddev: ${histogram.stddev.toFixed(2)}ns`);
// Percentiles console.log('\nPercentiles:');
[1, 10, 50, 90, 99, 99.9].forEach((p) => {
console.log(` p${p}: ${histogram.percentile(p).toFixed(2)}ns`);
});
});Event loop monitoring is particularly useful for detecting when your application might be experiencing issues with responsiveness due to long-running tasks blocking the event loop.
Tracking performance in asynchronous operations requires careful mark placement:
const { performance, PerformanceObserver } = require('perf_hooks');
const fs = require('fs');
// Create observer for the measures const obs = new PerformanceObserver((items) => {
items.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
obs.observe({ entryTypes: ['measure'] });
// Measure async file read operation performance.mark('readStart');
fs.readFile(__filename, (err, data) => {
if (err) throw err;
performance.mark('readEnd');
performance.measure('File Read', 'readStart', 'readEnd');
// Measure async processing time performance.mark('processStart');
// Simulate processing the file data setTimeout(() => {
const lines = data.toString().split('\n').length;
performance.mark('processEnd');
performance.measure('File Processing', 'processStart', 'processEnd');
console.log(`File has ${lines} lines`);
// Clean up performance.clearMarks();
performance.clearMeasures();
}, 100);
});Measuring the performance of promises requires similar techniques:
const { performance, PerformanceObserver } = require('perf_hooks');
// Set up the observer const obs = new PerformanceObserver((items) => {
items.getEntries().forEach((entry) => {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
});
});
obs.observe({ entryTypes: ['measure'] });
// Function that returns a promise function fetchData(delay) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ data: 'Sample data' });
}, delay);
});
}
// Function to process data function processData(data) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ processed: data.data.toUpperCase() });
}, 200);
});
}
// Measure Promise chain async function run() {
performance.mark('fetchStart');
const data = await fetchData(300);
performance.mark('fetchEnd');
performance.mark('processStart');
const processed = await processData(data);
performance.mark('processEnd');
// Create measures performance.measure('Fetch Data', 'fetchStart', 'fetchEnd');
performance.measure('Process Data', 'processStart', 'processEnd');
performance.measure('Total Operation', 'fetchStart', 'processEnd');
console.log('Result:', processed);
}
run().finally(() => {
// Clear after execution performance.clearMarks();
performance.clearMeasures();
});When using performance APIs, be aware of certain caveats:
JavaScript JIT compilation can cause inconsistent first-run times const { performance } = require('perf_hooks');
// For accurate benchmarking, perform multiple runs function benchmark(fn, iterations = 1000) {Formula
// Warm - up run (for JIT optimization)fn();
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
const end = performance.now();
times.push(end - start);
}
// Calculate statistics times.sort((a, b) => a - b);
const sum = times.reduce((a, b) => a + b, 0);
const avg = sum / times.length;
const median = times[Math.floor(times.length / 2)];
const min = times[0];
const max = times[times.length - 1];
return {average: avg, median: median, min: min, max: max, samples: times.length
};
}
// Example usage function testFunction() {
// Function to benchmark let x = 0;
for (let i = 0; i < 10000; i++) {
x += i;
}
return x;
}
const results = benchmark(testFunction);
console.log('Benchmark Results:');
console.log(` Samples: ${results.samples}`);
console.log(` Average: ${results.average.toFixed(4)}ms`);
console.log(` Median: ${results.median.toFixed(4)}ms`);
console.log(` Min: ${results.min.toFixed(4)}ms`);
console.log(` Max: ${results.max.toFixed(4)}ms`);The Node.js Performance Hooks API is based on the W3C Performance Timeline specification, but there are some differences compared to the browser's Performance API: