How to measure execution time of a function in milliseconds
I need to measure the time taken by a function to execute, specifically in milliseconds.
Note: This question was originally asked in 2008. While the accepted answer at that time was to use new Date().getTime(), the current best practice is to use the standard performance.now() API, which provides higher precision for performance measurement.
The most reliable way to measure function execution time in milliseconds is to use the performance.now() API, which provides high-resolution timing with sub-millisecond precision. This method captures the time before and after function execution, then calculates the difference to get the exact duration in milliseconds. Unlike older approaches using Date.now(), performance.now() is designed specifically for performance measurement and offers better accuracy for benchmarking.
Contents
- Basic Implementation
- Browser Compatibility
- Advanced Techniques
- Cross-Browser Solutions
- Node.js Alternatives
- Best Practices
Basic Implementation
The fundamental approach to measuring execution time involves capturing timestamps before and after function execution:
const start = performance.now();
// Your function to measure
functionToBeMeasured();
const end = performance.now();
const executionTime = end - start;
console.log(`Execution time: ${executionTime} ms`);
For async functions, the pattern remains similar:
const start = performance.now();
await asyncFunctionToMeasure();
const end = performance.now();
console.log(`Execution time: ${end - start} ms`);
The performance.now() method returns a DOMHighResTimeStamp value representing the time in milliseconds since the page started loading, but with much higher precision than Date.now(). This makes it ideal for measuring short execution times accurately.
Browser Compatibility
While performance.now() is widely supported across modern browsers, there are important compatibility considerations:
Browser Support:
- Chrome: Full support with high precision
- Firefox: Available but may round to 1-2 milliseconds increments due to security mitigations
- Safari: Available but may have implementation differences
- Edge: Full support following Chrome’s engine
Precision Variations:
According to the Mozilla Developer Network, browsers may round the results for security reasons. Firefox, for instance, started rounding to 2 milliseconds in Firefox 59 to mitigate Spectre vulnerabilities.
Sleep Behavior Differences:
The specification requires that performance.now() should continue ticking when the operating system sleeps or the browser process freezes. However, this behavior varies:
- Windows browsers typically continue timing during sleep
- Other platforms may pause timing during system sleep
This can significantly affect measurements for long-running operations, especially on mobile devices or laptops.
Advanced Techniques
For more sophisticated performance measurement, consider these approaches:
Measuring Multiple Functions:
function measureFunctionTime(fn, iterations = 1) {
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
const end = performance.now();
times.push(end - start);
}
return {
average: times.reduce((a, b) => a + b, 0) / times.length,
min: Math.min(...times),
max: Math.max(...times),
all: times
};
}
Using Performance Marks:
performance.mark('start-measurement');
yourFunction();
performance.mark('end-measurement');
performance.measure('function-execution', 'start-measurement', 'end-measurement');
const measures = performance.getEntriesByName('function-execution');
console.log(`Execution time: ${measures[0].duration} ms`);
This approach integrates with the Performance API, allowing for more comprehensive performance analysis.
Cross-Browser Solutions
For maximum compatibility across different browsers, use a polyfill or detection method:
Basic Detection:
var performance = window.performance || {};
performance.now = performance.now || performance.webkitNow ||
performance.msNow || performance.oNow ||
performance.mozNow || function() {
return Date.now();
};
More Robust Polyfill:
if (!window.performance) {
window.performance = {};
}
if (!window.performance.now) {
var start = Date.now();
window.performance.now = function() {
return Date.now() - start;
};
}
According to Stack Overflow discussions, these polyfills ensure consistent behavior across browsers, though they may not provide the same level of precision as native implementations.
Node.js Alternatives
In Node.js environments, you have several options for timing measurements:
Using process.hrtime:
const start = process.hrtime();
yourFunction();
const [seconds, nanoseconds] = process.hrtime(start);
const milliseconds = seconds * 1000 + nanoseconds / 1000000;
console.log(`Execution time: ${milliseconds} ms`);
Using performance.now() in Node.js:
const { performance } = require('perf_hooks');
const start = performance.now();
yourFunction();
const end = performance.now();
console.log(`Execution time: ${end - start} ms`);
BigInt Version (Highest Precision):
const start = process.hrtime.bigint();
yourFunction();
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1000000n;
console.log(`Execution time: ${duration} ms`);
The BigInt version provides nanosecond precision, making it suitable for very precise measurements.
Best Practices
When measuring function execution time, follow these best practices:
1. Run Multiple Measurements:
Single measurements can be affected by system noise. Run functions multiple times and calculate averages:
function benchmark(fn, iterations = 1000) {
const times = [];
for (let i = 0; i < iterations; i++) {
const start = performance.now();
fn();
times.push(performance.now() - start);
}
return {
average: times.reduce((a, b) => a + b, 0) / times.length,
min: Math.min(...times),
max: Math.max(...times)
};
}
2. Consider Warm-up Effects:
JavaScript engines optimize code over time. Run your function a few times before starting measurements to allow for optimizations.
3. Handle System Variability:
Measure during consistent system conditions. Background processes, CPU throttling, and memory usage can all affect results.
4. Use Appropriate Precision:
For very fast functions, consider running them multiple times to get measurable results.
5. Avoid Measurement Overhead:
Ensure your measurement code itself doesn’t significantly impact the results. Keep measurement logic minimal.
6. Consider Environment:
Browser vs. Node.js, different browsers, and system conditions all affect measurement accuracy. Always consider the context of your measurements.
Sources
- How to measure time taken by a function to execute - Stack Overflow
- Measuring JavaScript Functions’ Performance — SitePoint
- How to Measure JavaScript Execution Time - DEV Community
- Measure execution times in browsers & Node.js - DEV Community
- Node.js Performance Hooks and Measurement APIs - Medium
- How to Measure Time Taken by a Function to Execute - W3Docs
- Measuring execution times in Javascript like a pro - Medium
- Performance: now() method - MDN
- performance.now() - Web APIs
- Is there a cross browser solution for performance.now() in Chrome? - Stack Overflow
Conclusion
Measuring function execution time in milliseconds is straightforward with modern JavaScript using performance.now(). This API provides high-resolution timing that’s specifically designed for performance measurement, offering better precision than traditional date-based approaches. When implementing timing measurements, always consider browser compatibility, run multiple iterations for accuracy, and account for system variables that might affect your results. For Node.js environments, process.hrtime.bigint() offers even higher precision with nanosecond accuracy. Remember that while these measurements are valuable for optimization, they should be used as one tool among many for understanding and improving application performance.